Skip to content

Commit

Permalink
Navigate to the super implementation (#1165)
Browse files Browse the repository at this point in the history
Signed-off-by: Jinbo Wang <[email protected]>
  • Loading branch information
testforstephen authored and fbricon committed Sep 17, 2019
1 parent 8c3859b commit 0ab9fba
Show file tree
Hide file tree
Showing 6 changed files with 257 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*******************************************************************************
* Copyright (c) 2019 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Microsoft Corporation - initial API and implementation
*******************************************************************************/

package org.eclipse.jdt.ls.core.internal.handlers;

import java.util.Collections;
import java.util.List;
import java.util.Objects;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.corext.util.MethodOverrideTester;
import org.eclipse.jdt.ls.core.internal.JDTUtils;
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.TextDocumentPositionParams;

public class FindLinksHandler {

public static List<? extends Location> findLinks(String linkType, TextDocumentPositionParams position, IProgressMonitor monitor) {
ITypeRoot unit = JDTUtils.resolveTypeRoot(position.getTextDocument().getUri());
if (unit != null && !monitor.isCanceled()) {
PreferenceManager preferenceManager = JavaLanguageServerPlugin.getInstance().getPreferencesManager();
try {
IJavaElement element = JDTUtils.findElementAtSelection(unit, position.getPosition().getLine(), position.getPosition().getCharacter(), preferenceManager, monitor);
if (Objects.equals(linkType, "superImplementation")) {
IMethod overriddenMethod = findOverriddenMethod(element, monitor);
if (overriddenMethod != null) {
Location location = NavigateToDefinitionHandler.computeDefinitionNavigation(overriddenMethod, element.getJavaProject());
if (location != null) {
String declaringTypeName = overriddenMethod.getDeclaringType().getFullyQualifiedName();
String methodName = overriddenMethod.getElementName();
String displayName = declaringTypeName + "." + methodName;
return Collections.singletonList(new LinkLocation(displayName, "method", location));
}
}
}
} catch (JavaModelException e) {
// do nothing
}
}

return Collections.emptyList();
}

public static IMethod findOverriddenMethod(IJavaElement element, IProgressMonitor monitor) throws JavaModelException {
if (!(element instanceof IMethod)) {
return null;
}

IMethod method = (IMethod) element;
IType type = method.getDeclaringType();
if (type == null || type.isInterface() || method.isConstructor()) {
return null;
}

ITypeHierarchy hierarchy = type.newSupertypeHierarchy(monitor);
MethodOverrideTester tester = new MethodOverrideTester(type, hierarchy);
IMethod found = tester.findOverriddenMethod(method, true);
if (found != null && !found.equals(method)) {
return found;
}

return null;
}

public static class LinkLocation extends Location {
public String displayName;
public String kind;

public LinkLocation(String displayName, String kind, Location location) {
super(location.getUri(), location.getRange());
this.displayName = displayName;
this.kind = kind;
}
}

public static class FindLinksParams {
// Supported link types: superImplementation
public String type;
public TextDocumentPositionParams position;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ InitializeResult initialize(InitializeParams param) {
if (!preferenceManager.getClientPreferences().isTypeDefinitionDynamicRegistered()) {
capabilities.setTypeDefinitionProvider(Boolean.TRUE);
}
if (!preferenceManager.getClientPreferences().isHoverDynamicRegistered()) {
if (!preferenceManager.getClientPreferences().isClientHoverProviderRegistered() && !preferenceManager.getClientPreferences().isHoverDynamicRegistered()) {
capabilities.setHoverProvider(Boolean.TRUE);
}
if (!preferenceManager.getClientPreferences().isReferencesDynamicRegistered()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
import org.eclipse.jdt.ls.core.internal.LanguageServerWorkingCopyOwner;
import org.eclipse.jdt.ls.core.internal.ServiceStatus;
import org.eclipse.jdt.ls.core.internal.codemanipulation.GenerateGetterSetterOperation.AccessorField;
import org.eclipse.jdt.ls.core.internal.handlers.FindLinksHandler.FindLinksParams;
import org.eclipse.jdt.ls.core.internal.handlers.GenerateAccessorsHandler.GenerateAccessorsParams;
import org.eclipse.jdt.ls.core.internal.handlers.GenerateConstructorsHandler.CheckConstructorsResponse;
import org.eclipse.jdt.ls.core.internal.handlers.GenerateConstructorsHandler.GenerateConstructorsParams;
Expand Down Expand Up @@ -229,7 +230,7 @@ public void initialized(InitializedParams params) {
if (preferenceManager.getClientPreferences().isTypeDefinitionDynamicRegistered()) {
registerCapability(Preferences.TYPEDEFINITION_ID, Preferences.TYPEDEFINITION);
}
if (preferenceManager.getClientPreferences().isHoverDynamicRegistered()) {
if (!preferenceManager.getClientPreferences().isClientHoverProviderRegistered() && preferenceManager.getClientPreferences().isHoverDynamicRegistered()) {
registerCapability(Preferences.HOVER_ID, Preferences.HOVER);
}
if (preferenceManager.getClientPreferences().isReferencesDynamicRegistered()) {
Expand Down Expand Up @@ -562,6 +563,11 @@ public CompletableFuture<List<? extends Location>> references(ReferenceParams pa
return computeAsync((monitor) -> handler.findReferences(params, monitor));
}

public CompletableFuture<List<? extends Location>> findLinks(FindLinksParams params) {
logInfo(">> java/findLinks");
return computeAsync((monitor) -> FindLinksHandler.findLinks(params.type, params.position, monitor));
}

/* (non-Javadoc)
* @see org.eclipse.lsp4j.services.TextDocumentService#documentHighlight(org.eclipse.lsp4j.TextDocumentPositionParams)
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import org.eclipse.jdt.ls.core.internal.BuildWorkspaceStatus;
import org.eclipse.jdt.ls.core.internal.codemanipulation.GenerateGetterSetterOperation.AccessorField;
import org.eclipse.jdt.ls.core.internal.handlers.FindLinksHandler.FindLinksParams;
import org.eclipse.jdt.ls.core.internal.handlers.GenerateAccessorsHandler.GenerateAccessorsParams;
import org.eclipse.jdt.ls.core.internal.handlers.GenerateConstructorsHandler.CheckConstructorsResponse;
import org.eclipse.jdt.ls.core.internal.handlers.GenerateConstructorsHandler.GenerateConstructorsParams;
Expand All @@ -32,8 +33,10 @@
import org.eclipse.jdt.ls.core.internal.handlers.OverrideMethodsHandler.OverridableMethodsResponse;
import org.eclipse.jdt.ls.core.internal.handlers.WorkspaceSymbolHandler.SearchSymbolParams;
import org.eclipse.lsp4j.CodeActionParams;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.SymbolInformation;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.TextDocumentPositionParams;
import org.eclipse.lsp4j.WorkspaceEdit;
import org.eclipse.lsp4j.jsonrpc.services.JsonNotification;
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest;
Expand Down Expand Up @@ -111,4 +114,7 @@ public interface JavaProtocolExtensions {

@JsonRequest
CompletableFuture<List<SymbolInformation>> searchSymbols(SearchSymbolParams params);

@JsonRequest
CompletableFuture<List<? extends Location>> findLinks(FindLinksParams params);
}
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,10 @@ public boolean isMoveRefactoringSupported() {
return Boolean.parseBoolean(extendedClientCapabilities.getOrDefault("moveRefactoringSupport", "false").toString());
}

public boolean isClientHoverProviderRegistered() {
return Boolean.parseBoolean(extendedClientCapabilities.getOrDefault("clientHoverProvider", "false").toString());
}

public boolean isSupportsCompletionDocumentationMarkdown() {
//@formatter:off
return v3supported && capabilities.getTextDocument().getCompletion() != null
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*******************************************************************************
* Copyright (c) 2019 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Microsoft Corporation - initial API and implementation
*******************************************************************************/

package org.eclipse.jdt.ls.core.internal.handlers;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;

import java.util.List;

import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.ls.core.internal.JDTUtils;
import org.eclipse.jdt.ls.core.internal.handlers.FindLinksHandler.LinkLocation;
import org.eclipse.jdt.ls.core.internal.managers.AbstractProjectsManagerBasedTest;
import org.eclipse.jdt.ls.core.internal.preferences.ClientPreferences;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.TextDocumentPositionParams;
import org.junit.Before;
import org.junit.Test;

public class FindLinksHandlerTest extends AbstractProjectsManagerBasedTest {
private IPackageFragmentRoot sourceFolder;

@Before
public void setup() throws Exception {
IJavaProject javaProject = newEmptyProject();
sourceFolder = javaProject.getPackageFragmentRoot(javaProject.getProject().getFolder("src"));
ClientPreferences clientPreferences = preferenceManager.getClientPreferences();
when(clientPreferences.isResourceOperationSupported()).thenReturn(true);
}

@Test
public void testFindSuperMethod() throws JavaModelException {
IPackageFragment pack1 = sourceFolder.createPackageFragment("test1", false, null);
//@formatter:off
ICompilationUnit unitA = pack1.createCompilationUnit("A.java", "package test1;\n" +
"\n" +
"public class A {\n" +
" public void run() {\n" +
" }\n" +
"}", true, null);
//@formatter:on

//@formatter:off
ICompilationUnit unitB = pack1.createCompilationUnit("B.java", "package test1;\n" +
"\n" +
"public class B extends A {\n" +
" public void run() {\n" +
" }\n" +
"}", true, null);
//@formatter:on

String uri = JDTUtils.toURI(unitB);
List<? extends Location> response = FindLinksHandler.findLinks("superImplementation", new TextDocumentPositionParams(new TextDocumentIdentifier(uri), new Position(3, 14)), new NullProgressMonitor());
assertTrue(response != null && response.size() == 1);
LinkLocation location = (LinkLocation) response.get(0);
assertEquals("test1.A.run", location.displayName);
assertEquals("method", location.kind);
assertEquals(JDTUtils.toURI(unitA), location.getUri());
Range range = location.getRange();
assertEquals(3, range.getStart().getLine());
assertEquals(13, range.getStart().getCharacter());
assertEquals(3, range.getEnd().getLine());
assertEquals(16, range.getEnd().getCharacter());
}

@Test
public void testFindNearestSuperMethod() throws JavaModelException {
IPackageFragment pack1 = sourceFolder.createPackageFragment("test1", false, null);
//@formatter:off
ICompilationUnit unitA = pack1.createCompilationUnit("A.java", "package test1;\n" +
"\n" +
"public class A {\n" +
" public void run() {\n" +
" }\n" +
"}", true, null);
//@formatter:on

//@formatter:off
ICompilationUnit unitB = pack1.createCompilationUnit("B.java", "package test1;\n" +
"\n" +
"public class B extends A {\n" +
"}", true, null);
//@formatter:on

//@formatter:off
ICompilationUnit unitC = pack1.createCompilationUnit("C.java", "package test1;\n" +
"\n" +
"public class C extends B {\n" +
" public void run() {\n" +
" }\n" +
"}", true, null);
//@formatter:on

String uri = JDTUtils.toURI(unitC);
List<? extends Location> response = FindLinksHandler.findLinks("superImplementation", new TextDocumentPositionParams(new TextDocumentIdentifier(uri), new Position(3, 14)), new NullProgressMonitor());
assertTrue(response != null && response.size() == 1);
LinkLocation location = (LinkLocation) response.get(0);
assertEquals("test1.A.run", location.displayName);
assertEquals("method", location.kind);
assertEquals(JDTUtils.toURI(unitA), location.getUri());
Range range = location.getRange();
assertEquals(3, range.getStart().getLine());
assertEquals(13, range.getStart().getCharacter());
assertEquals(3, range.getEnd().getLine());
assertEquals(16, range.getEnd().getCharacter());
}

@Test
public void testNoSuperMethod() throws JavaModelException {
IPackageFragment pack1 = sourceFolder.createPackageFragment("test1", false, null);
//@formatter:off
ICompilationUnit unitA = pack1.createCompilationUnit("A.java", "package test1;\n" +
"\n" +
"public class A {\n" +
" public void run() {\n" +
" }\n" +
"}", true, null);
//@formatter:on

String uri = JDTUtils.toURI(unitA);
List<? extends Location> response = FindLinksHandler.findLinks("superImplementation", new TextDocumentPositionParams(new TextDocumentIdentifier(uri), new Position(3, 14)), new NullProgressMonitor());
assertTrue(response == null || response.isEmpty());
}
}

0 comments on commit 0ab9fba

Please sign in to comment.