From 2221df66bc395be256eec006fdac7e26da964884 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Tue, 14 May 2024 10:16:11 +0200 Subject: [PATCH] Filtering TextDocumentService.codeAction results based on the provided kinds. --- .../db/MicronautDataEndpointGenerator.java | 7 + .../db/MicronautEndpointTestGenerator.java | 6 + ide/api.lsp/apichanges.xml | 13 ++ ide/api.lsp/manifest.mf | 2 +- .../netbeans/spi/lsp/CodeActionProvider.java | 13 ++ .../modules/java/lsp/server/Utils.java | 7 + .../server/protocol/CodeActionsProvider.java | 5 + .../CodeActionsProvider2LspApiBridge.java | 25 ++++ .../server/protocol/IntroduceCodeActions.java | 126 ++++++++++++++++++ .../protocol/TextDocumentServiceImpl.java | 66 +++------ .../java/lsp/server/protocol/ServerTest.java | 89 +++++++++++++ 11 files changed, 311 insertions(+), 48 deletions(-) create mode 100644 java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/IntroduceCodeActions.java 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 184fe69be3fa..5547c9f3887c 100644 --- a/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/MicronautDataEndpointGenerator.java +++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/MicronautDataEndpointGenerator.java @@ -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; @@ -93,6 +94,7 @@ public class MicronautDataEndpointGenerator implements CodeActionProvider, CommandProvider { private static final String SOURCE = "source"; + private static final Set 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"; @@ -107,6 +109,11 @@ public class MicronautDataEndpointGenerator implements CodeActionProvider, Comma return new NotifyDescriptor.QuickPick.Item(label, description); }).create(); + @Override + public Set getSupportedCodeActionKinds() { + return SUPPORTED_CODE_ACTION_KINDS; + } + @Override @NbBundle.Messages({ "DN_GenerateDataEndpoint=Generate Data Endpoint...", diff --git a/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/MicronautEndpointTestGenerator.java b/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/MicronautEndpointTestGenerator.java index 0c61b9d4ee12..316088436f78 100644 --- a/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/MicronautEndpointTestGenerator.java +++ b/enterprise/micronaut/src/org/netbeans/modules/micronaut/db/MicronautEndpointTestGenerator.java @@ -117,6 +117,7 @@ public class MicronautEndpointTestGenerator implements CodeActionProvider, CommandProvider { private static final String SOURCE = "source"; + private static final Set 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"; @@ -148,6 +149,11 @@ public class MicronautEndpointTestGenerator implements CodeActionProvider, Comma return new NotifyDescriptor.QuickPick.Item(label, description); }).create(); + @Override + public Set getSupportedCodeActionKinds() { + return SUPPORTED_CODE_ACTION_KINDS; + } + @Override @NbBundle.Messages({ "DN_GenerateEndpointTest=Generate Micronaut Endpoint Tests...", diff --git a/ide/api.lsp/apichanges.xml b/ide/api.lsp/apichanges.xml index faa0a1b27335..d3cbe5631476 100644 --- a/ide/api.lsp/apichanges.xml +++ b/ide/api.lsp/apichanges.xml @@ -51,6 +51,19 @@ + + + Adding CodeActionProvider.getSupportedCodeActionKinds method + + + + + + A CodeActionProvider.getSupportedCodeActionKinds method is + introduced that allows to specify supported kinds of code actions. + + + Adding ErrorProvider.Context.getHintsConfigFile() method diff --git a/ide/api.lsp/manifest.mf b/ide/api.lsp/manifest.mf index ca8e28f14b05..476426e3b2eb 100644 --- a/ide/api.lsp/manifest.mf +++ b/ide/api.lsp/manifest.mf @@ -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 diff --git a/ide/api.lsp/src/org/netbeans/spi/lsp/CodeActionProvider.java b/ide/api.lsp/src/org/netbeans/spi/lsp/CodeActionProvider.java index b234018a0d50..3006803ebd9b 100644 --- a/ide/api.lsp/src/org/netbeans/spi/lsp/CodeActionProvider.java +++ b/ide/api.lsp/src/org/netbeans/spi/lsp/CodeActionProvider.java @@ -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; @@ -42,4 +44,15 @@ public interface CodeActionProvider { * @since 1.23 */ public List 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 getSupportedCodeActionKinds() { + return null; + } } 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 d818c9315f71..bb69242e7cf0 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 @@ -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; @@ -571,4 +572,10 @@ public static WorkspaceEdit workspaceEditFromApi(org.netbeans.api.lsp.WorkspaceE } return new WorkspaceEdit(documentChanges); } + + public static Predicate codeActionKindFilter(List only) { + return k -> only == null || + only.stream() + .anyMatch(o -> k.equals(o) || k.startsWith(o + ".")); + } } 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 47eccb3455dd..5a167e313edc 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 @@ -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; @@ -54,6 +55,10 @@ public abstract class CodeActionsProvider { public static final String DATA = "data"; protected static final String ERROR = ""; //NOI18N + public @CheckForNull Set getSupportedCodeActionKinds() { + return null; + } + public abstract List getCodeActions(NbCodeLanguageClient client, ResultIterator resultIterator, CodeActionParams params) throws Exception; public CompletableFuture resolve(NbCodeLanguageClient client, CodeAction codeAction, Object data) { 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 7b7f70e9e272..6144650c7c53 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 @@ -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; @@ -72,6 +73,23 @@ public CompletableFuture processCommand(NbCodeLanguageClient client, Str return CompletableFuture.completedFuture(false); } + @Override + public Set getSupportedCodeActionKinds() { + Set supportedCodeActionKinds = new HashSet<>(); + + for (CodeActionProvider caProvider : Lookup.getDefault().lookupAll(CodeActionProvider.class)) { + Set providerSupportedCodeActionKinds = caProvider.getSupportedCodeActionKinds(); + + if (providerSupportedCodeActionKinds == null) { + return null; + } + + supportedCodeActionKinds.addAll(providerSupportedCodeActionKinds); + } + + return supportedCodeActionKinds; + } + @Override public List getCodeActions(NbCodeLanguageClient client, ResultIterator resultIterator, CodeActionParams params) throws Exception { lastCodeActions = new ArrayList<>(); @@ -84,7 +102,14 @@ public List getCodeActions(NbCodeLanguageClient client, ResultIterat org.netbeans.api.lsp.Range r = new org.netbeans.api.lsp.Range(startOffset, endOffset); List only = params.getContext().getOnly(); Lookup l = only != null ? Lookups.fixed(client, resultIterator, only) : Lookups.fixed(client, resultIterator); + Predicate codeActionKindPermitted = Utils.codeActionKindFilter(only); for (CodeActionProvider caProvider : Lookup.getDefault().lookupAll(CodeActionProvider.class)) { + Set 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; diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/IntroduceCodeActions.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/IntroduceCodeActions.java new file mode 100644 index 000000000000..8fa35abf9038 --- /dev/null +++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/IntroduceCodeActions.java @@ -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 SUPPORTED_CODE_ACTION_KINDS = + Collections.unmodifiableSet(new HashSet<>(Arrays.asList(CodeActionKind.RefactorExtract))); + + public IntroduceCodeActions() { + } + + @Override + public Set getSupportedCodeActionKinds() { + return SUPPORTED_CODE_ACTION_KINDS; + } + + @Override + public List getCodeActions(NbCodeLanguageClient client, ResultIterator resultIterator, CodeActionParams params) throws Exception { + Range range = params.getRange(); + List 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.class), new EnumMap(IntroduceKind.class), new AtomicBoolean())) { + for (Fix fix : err.getFixes().getFixes()) { + if (fix instanceof IntroduceFixBase) { + try { + ModificationResult changes = ((IntroduceFixBase) fix).getModificationResult(); + if (changes != null) { + List> documentChanges = new ArrayList<>(); + Set fos = changes.getModifiedFileObjects(); + if (fos.size() == 1) { + FileObject fileObject = fos.iterator().next(); + List diffs = changes.getDifferences(fileObject); + if (diffs != null) { + List 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; + } + +} 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 b33ff46f4643..e0cb2c215827 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 @@ -67,6 +67,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import java.util.function.IntFunction; +import java.util.function.Predicate; import java.util.logging.Level; import java.util.logging.Logger; import java.util.prefs.Preferences; @@ -92,6 +93,7 @@ import org.eclipse.lsp4j.ClientCapabilities; import org.eclipse.lsp4j.CodeAction; import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4j.CodeActionKindCapabilities; import org.eclipse.lsp4j.CodeActionParams; import org.eclipse.lsp4j.CodeLens; import org.eclipse.lsp4j.CodeLensParams; @@ -991,7 +993,9 @@ public CompletableFuture>> codeAction(CodeActio Range range = params.getRange(); int startOffset = Utils.getOffset(doc, range.getStart()); int endOffset = Utils.getOffset(doc, range.getEnd()); - if (startOffset == endOffset || !params.getContext().getDiagnostics().isEmpty()) { + Predicate codeActionKindPermitted = Utils.codeActionKindFilter(params.getContext().getOnly()); + if ((startOffset == endOffset || !params.getContext().getDiagnostics().isEmpty()) && + (codeActionKindPermitted.test(CodeActionKind.QuickFix) || codeActionKindPermitted.test(CodeActionKind.RefactorRewrite))) { final javax.swing.text.Element elem = NbDocument.findLineRootElement(doc); int lineStartOffset = elem.getStartOffset(); int lineEndOffset = elem.getEndOffset(); @@ -1031,7 +1035,11 @@ public CompletableFuture>> codeAction(CodeActio if (diag.isPresent()) { action.setDiagnostics(Collections.singletonList(diag.get())); } - action.setKind(kind(err.getSeverity())); + String codeActionKind = kind(err.getSeverity()); + if (!codeActionKindPermitted.test(codeActionKind)) { + continue; + } + action.setKind(codeActionKind); if (inputAction.getCommand() != null) { List commandParams = new ArrayList<>(); @@ -1068,59 +1076,23 @@ public CompletableFuture>> codeAction(CodeActio public void run(ResultIterator resultIterator) throws Exception { //code generators: for (CodeActionsProvider codeGenerator : Lookup.getDefault().lookupAll(CodeActionsProvider.class)) { + Set supportedCodeActionKinds = codeGenerator.getSupportedCodeActionKinds(); + if (supportedCodeActionKinds != null && + supportedCodeActionKinds.stream() + .noneMatch(kind -> codeActionKindPermitted.test(kind))) { + continue; + } try { for (CodeAction codeAction : codeGenerator.getCodeActions(client, resultIterator, params)) { + if (!codeActionKindPermitted.test(codeAction.getKind())) { + continue; + } result.add(Either.forRight(codeAction)); } } catch (Exception ex) { client.logMessage(new MessageParams(MessageType.Error, ex.getMessage())); } } - if (client.getNbCodeCapabilities().wantsJavaSupport()) { - //introduce hints: - CompilationController cc = resultIterator.getParserResult() != null ? CompilationController.get(resultIterator.getParserResult()) : null; - if (cc != null) { - cc.toPhase(JavaSource.Phase.RESOLVED); - if (!range.getStart().equals(range.getEnd())) { - for (ErrorDescription err : IntroduceHint.computeError(cc, startOffset, endOffset, new EnumMap(IntroduceKind.class), new EnumMap(IntroduceKind.class), new AtomicBoolean())) { - for (Fix fix : err.getFixes().getFixes()) { - if (fix instanceof IntroduceFixBase) { - try { - ModificationResult changes = ((IntroduceFixBase) fix).getModificationResult(); - if (changes != null) { - List> documentChanges = new ArrayList<>(); - Set fos = changes.getModifiedFileObjects(); - if (fos.size() == 1) { - FileObject fileObject = fos.iterator().next(); - List diffs = changes.getDifferences(fileObject); - if (diffs != null) { - List 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(Either.forRight(codeAction)); - } - } - } catch (GeneratorUtils.DuplicateMemberException dme) { - } - } - } - } - } - } - } } }); } catch (ParseException 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 1b8d71ec96fb..78b327fbec90 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 @@ -5786,6 +5786,95 @@ public void testDefaultLookupJustOnce() throws Exception { assertEquals(1, mm4.size()); } + public void testErrorBasedCodeActionFiltering() throws Exception { + File src = new File(getWorkDir(), "Test.java"); + src.getParentFile().mkdirs(); + String code = "public class Test {\n" + + " public void test() {\n" + + " System.err.println(0 << 0);\n" + + " }\n" + + "}\n"; + try (Writer w = new FileWriter(src)) { + w.write(code); + } + + List[] diags = new List[1]; + Launcher serverLauncher = createClientLauncherWithLogging(new LspClient() { + private int publishedDiagnosticsCount; + @Override + public void telemetryEvent(Object arg0) { + } + + @Override + public void publishDiagnostics(PublishDiagnosticsParams params) { + synchronized (diags) { + if (publishedDiagnosticsCount++ == 1) { + diags[0] = params.getDiagnostics(); + diags.notifyAll(); + } + } + } + + @Override + public void showMessage(MessageParams arg0) { + } + + @Override + public CompletableFuture showMessageRequest(ShowMessageRequestParams arg0) { + return CompletableFuture.completedFuture(new MessageActionItem(arg0.getActions().get(0).getTitle())); + } + + @Override + public void logMessage(MessageParams arg0) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public CompletableFuture applyEdit(ApplyWorkspaceEditParams params) { + 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))); + synchronized (diags) { + while (diags[0] == null) { + try { + diags.wait(); + } catch (InterruptedException ex) { + } + } + } + VersionedTextDocumentIdentifier id = new VersionedTextDocumentIdentifier(src.toURI().toString(), 1); + Set presentKinds; + List> codeActions; + codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 32), new Position(2, 32)), new CodeActionContext(Collections.emptyList(), null))).get(); + presentKinds = codeActions.stream().map(d -> d.getRight().getKind()).collect(Collectors.toSet()); + assertEquals(new HashSet<>(Arrays.asList(CodeActionKind.RefactorRewrite, CodeActionKind.QuickFix)), presentKinds); + codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 32), new Position(2, 32)), new CodeActionContext(Collections.emptyList(), Arrays.asList(CodeActionKind.RefactorRewrite)))).get(); + presentKinds = codeActions.stream().map(d -> d.getRight().getKind()).collect(Collectors.toSet()); + assertEquals(new HashSet<>(Arrays.asList(CodeActionKind.RefactorRewrite)), presentKinds); + codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 32), new Position(2, 32)), new CodeActionContext(Collections.emptyList(), Arrays.asList(CodeActionKind.Refactor)))).get(); + presentKinds = codeActions.stream().map(d -> d.getRight().getKind()).collect(Collectors.toSet()); + assertEquals(new HashSet<>(Arrays.asList(CodeActionKind.RefactorRewrite)), presentKinds); + codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 32), new Position(2, 32)), new CodeActionContext(Collections.emptyList(), Arrays.asList(CodeActionKind.QuickFix)))).get(); + presentKinds = codeActions.stream().map(d -> d.getRight().getKind()).collect(Collectors.toSet()); + assertEquals(new HashSet<>(Arrays.asList(CodeActionKind.QuickFix)), presentKinds); + codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 32), new Position(2, 32)), new CodeActionContext(Collections.emptyList(), Arrays.asList(CodeActionKind.RefactorExtract)))).get(); + presentKinds = codeActions.stream().map(d -> d.getRight().getKind()).collect(Collectors.toSet()); + assertEquals(new HashSet<>(Arrays.asList()), presentKinds); + codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 27), new Position(2, 33)), new CodeActionContext(Collections.emptyList(), Arrays.asList(CodeActionKind.RefactorExtract)))).get(); + presentKinds = codeActions.stream().map(d -> d.getRight().getKind()).collect(Collectors.toSet()); + assertEquals(new HashSet<>(Arrays.asList(CodeActionKind.RefactorExtract)), presentKinds); + //verify surround-with hints are correctly filtered: + codeActions = server.getTextDocumentService().codeAction(new CodeActionParams(id, new Range(new Position(2, 0), new Position(3, 0)), new CodeActionContext(Collections.emptyList(), Arrays.asList(CodeActionKind.RefactorExtract)))).get(); + presentKinds = codeActions.stream().map(d -> d.getRight().getKind()).collect(Collectors.toSet()); + assertEquals(new HashSet<>(Arrays.asList(CodeActionKind.RefactorExtract)), presentKinds); + } + static { System.setProperty("SourcePath.no.source.filter", "true"); JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = true;