-
Notifications
You must be signed in to change notification settings - Fork 408
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Protocol extension for 'implementors' #379
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
/******************************************************************************* | ||
* Copyright (c) 2016-2017 Red Hat Inc. 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: | ||
* Red Hat Inc. - initial API and implementation | ||
*******************************************************************************/ | ||
package org.eclipse.jdt.ls.core.internal.handlers; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
import org.eclipse.core.runtime.IProgressMonitor; | ||
import org.eclipse.jdt.core.ICompilationUnit; | ||
import org.eclipse.jdt.core.IJavaElement; | ||
import org.eclipse.jdt.core.IType; | ||
import org.eclipse.jdt.core.ITypeRoot; | ||
import org.eclipse.jdt.core.JavaModelException; | ||
import org.eclipse.jdt.ls.core.internal.JDTUtils; | ||
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; | ||
import org.eclipse.jdt.ls.core.internal.ResourceUtils; | ||
import org.eclipse.jdt.ls.core.internal.hover.JavaElementLabels; | ||
import org.eclipse.lsp4j.Location; | ||
import org.eclipse.lsp4j.SymbolInformation; | ||
import org.eclipse.lsp4j.SymbolKind; | ||
import org.eclipse.lsp4j.TextDocumentPositionParams; | ||
|
||
public class ImplementorsHandler { | ||
|
||
public List<? extends SymbolInformation> implementors(TextDocumentPositionParams params, IProgressMonitor monitor) { | ||
ITypeRoot unit = JDTUtils.resolveTypeRoot(params.getTextDocument().getUri()); | ||
if (unit != null) { | ||
try { | ||
IJavaElement element = JDTUtils.findElementAtSelection(unit, params.getPosition().getLine(), params.getPosition().getCharacter()); | ||
if (element instanceof IType) { | ||
return findImplementations((IType) element, monitor); | ||
} | ||
} catch (JavaModelException e) { | ||
JavaLanguageServerPlugin.logException("Problem resolving implementors", e); | ||
} | ||
} | ||
return Collections.emptyList(); | ||
} | ||
|
||
private List<SymbolInformation> findImplementations(IType type, IProgressMonitor monitor) throws JavaModelException { | ||
IType[] results = type.newTypeHierarchy(monitor).getAllSubtypes(type); | ||
final List<SymbolInformation> symbols = new ArrayList<>(); | ||
for (IType t : results) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. return empty list if monitor is cancelled |
||
ICompilationUnit compilationUnit = (ICompilationUnit) t.getAncestor(IJavaElement.COMPILATION_UNIT); | ||
if (compilationUnit == null) { | ||
continue; | ||
} | ||
Location location = JDTUtils.toLocation(t); | ||
if (location != null) { | ||
SymbolInformation si = new SymbolInformation(); | ||
String name = JavaElementLabels.getElementLabel(t, JavaElementLabels.ALL_DEFAULT); | ||
si.setName(name == null ? t.getElementName() : name); | ||
si.setKind(t.isInterface() ? SymbolKind.Interface : SymbolKind.Class); | ||
if (t.getParent() != null) { | ||
si.setContainerName(t.getParent().getElementName()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does t.getParent().getElementName() return a fcqn or simple name? We need to guarantee unambiguous parent hierarchy |
||
} | ||
location.setUri(ResourceUtils.toClientUri(location.getUri())); | ||
si.setLocation(location); | ||
if (!symbols.contains(si)) { | ||
symbols.add(si); | ||
} | ||
} | ||
} | ||
return symbols; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package org.sample; | ||
|
||
public abstract class A implements I { | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package org.sample; | ||
|
||
public class B extends A { | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package org.sample; | ||
|
||
public interface I { | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package org.sample; | ||
|
||
public interface I2 { | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package org.sample; | ||
|
||
public interface I3 extends I2 { | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
/******************************************************************************* | ||
* Copyright (c) 2016-2017 Red Hat Inc. 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: | ||
* Red Hat Inc. - initial API and implementation | ||
*******************************************************************************/ | ||
package org.eclipse.jdt.ls.core.internal.handlers; | ||
|
||
import static org.junit.Assert.assertSame; | ||
import static org.junit.Assert.assertTrue; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.when; | ||
|
||
import java.io.UnsupportedEncodingException; | ||
import java.util.List; | ||
import java.util.concurrent.ExecutionException; | ||
|
||
import org.eclipse.core.resources.IProject; | ||
import org.eclipse.jdt.core.JavaModelException; | ||
import org.eclipse.jdt.ls.core.internal.ClassFileUtil; | ||
import org.eclipse.jdt.ls.core.internal.WorkspaceHelper; | ||
import org.eclipse.jdt.ls.core.internal.managers.AbstractProjectsManagerBasedTest; | ||
import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager; | ||
import org.eclipse.jdt.ls.core.internal.preferences.Preferences; | ||
import org.eclipse.lsp4j.Position; | ||
import org.eclipse.lsp4j.Range; | ||
import org.eclipse.lsp4j.SymbolInformation; | ||
import org.eclipse.lsp4j.SymbolKind; | ||
import org.eclipse.lsp4j.TextDocumentIdentifier; | ||
import org.eclipse.lsp4j.TextDocumentPositionParams; | ||
import org.junit.Before; | ||
import org.junit.Test; | ||
|
||
/** | ||
* @author snjeza | ||
*/ | ||
public class ImplementorsHandlerTest extends AbstractProjectsManagerBasedTest { | ||
private IProject project; | ||
private ImplementorsHandler handler; | ||
|
||
@Before | ||
public void setup() throws Exception { | ||
importProjects("eclipse/hello"); | ||
project = WorkspaceHelper.getProject("hello"); | ||
preferenceManager = mock(PreferenceManager.class); | ||
when(preferenceManager.getPreferences()).thenReturn(new Preferences()); | ||
handler = new ImplementorsHandler(); | ||
} | ||
|
||
@Test | ||
public void testInterface() throws Exception { | ||
String className = "org.sample.I"; | ||
Position position = new Position(2, 18); | ||
List<? extends SymbolInformation> symbols = getSymbols(className, position); | ||
assertTrue(symbols.size() > 0); | ||
for (SymbolInformation symbol : symbols) { | ||
assertKind(SymbolKind.Class, symbol); | ||
assertTrue(isValid(symbol.getLocation().getRange())); | ||
} | ||
className = "org.sample.I2"; | ||
position = new Position(2, 19); | ||
symbols = getSymbols(className, position); | ||
assertTrue(symbols.size() > 0); | ||
for (SymbolInformation symbol : symbols) { | ||
assertKind(SymbolKind.Interface, symbol); | ||
assertTrue(isValid(symbol.getLocation().getRange())); | ||
} | ||
} | ||
|
||
@Test | ||
public void testClass() throws Exception { | ||
String className = "org.sample.A"; | ||
Position position = new Position(2, 23); | ||
List<? extends SymbolInformation> symbols = getSymbols(className, position); | ||
assertTrue(symbols.size() > 0); | ||
for (SymbolInformation symbol : symbols) { | ||
assertKind(SymbolKind.Class, symbol); | ||
assertTrue(isValid(symbol.getLocation().getRange())); | ||
} | ||
} | ||
|
||
private boolean isValid(Range range) { | ||
return range != null && isValid(range.getStart()) && isValid(range.getEnd()); | ||
} | ||
|
||
private boolean isValid(Position position) { | ||
return position != null && position.getLine() >= 0 && position.getCharacter() >= 0; | ||
} | ||
|
||
private void assertKind(SymbolKind expectedKind, SymbolInformation symbol) { | ||
assertSame("Unexpected SymbolKind in " + symbol.getName(), expectedKind, symbol.getKind()); | ||
} | ||
|
||
private List<? extends SymbolInformation> getSymbols(String className, Position position) | ||
throws JavaModelException, UnsupportedEncodingException, InterruptedException, ExecutionException { | ||
String uri = ClassFileUtil.getURI(project, className); | ||
TextDocumentIdentifier textDocument = new TextDocumentIdentifier(uri); | ||
TextDocumentPositionParams params = new TextDocumentPositionParams(textDocument, position); | ||
List<? extends SymbolInformation> symbols = handler.implementors(params, monitor); | ||
return symbols; | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably need something more open for the parameter, eg. what if the request only contains an FCQN? I think we should have only 1
java/implementors
entry point, but the incoming parameter should probably wrap TextDocumentPositionParams and some other stuff. Shooting from the hip:We need more info from @tsmaeder about potential usage of that API in Che.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we call it
java/typeHierarchy
instead?