Skip to content

Commit

Permalink
eclipse-jdtlsGH-28: Added support for the super- and subtype hierarch…
Browse files Browse the repository at this point in the history
…ies.

Closes: eclipse-jdtls#28.

Signed-off-by: Akos Kitta <[email protected]>
  • Loading branch information
Akos Kitta committed Oct 9, 2018
1 parent f00ef84 commit decc6f6
Show file tree
Hide file tree
Showing 9 changed files with 734 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,13 @@ ISourceRange getRange(IJavaElement element) throws JavaModelException {
};

/* default */ abstract ISourceRange getRange(IJavaElement element) throws JavaModelException;

/**
* Sugar for {@link JDTUtils#toLocation(IJavaElement, LocationType)}.
*/
public Location toLocation(IJavaElement element) throws JavaModelException {
return JDTUtils.toLocation(element, this);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@

import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.toList;
import static org.eclipse.jdt.core.IJavaElement.CLASS_FILE;
import static org.eclipse.jdt.core.IJavaElement.COMPILATION_UNIT;
import static org.eclipse.jdt.core.IJavaElement.FIELD;
import static org.eclipse.jdt.core.IJavaElement.METHOD;
import static org.eclipse.jdt.core.IJavaElement.PACKAGE_DECLARATION;
import static org.eclipse.jdt.core.IJavaElement.TYPE;
import static org.eclipse.jdt.ls.core.internal.JDTUtils.LocationType.FULL_RANGE;
import static org.eclipse.jdt.ls.core.internal.JDTUtils.LocationType.NAME_RANGE;
import static org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin.logInfo;
import static org.eclipse.jdt.ls.core.internal.hover.JavaElementLabels.ALL_DEFAULT;
import static org.eclipse.jdt.ls.core.internal.hover.JavaElementLabels.M_APP_RETURNTYPE;
Expand All @@ -31,16 +33,20 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IParent;
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.JDTUtils.LocationType;
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;
Expand Down Expand Up @@ -109,7 +115,7 @@ private void collectChildren(ITypeRoot unit, IJavaElement[] elements, ArrayList<
SymbolInformation si = new SymbolInformation();
String name = JavaElementLabels.getElementLabel(element, JavaElementLabels.ALL_DEFAULT);
si.setName(name == null ? element.getElementName() : name);
si.setKind(mapKind(element));
si.setKind(getSymbolKind(element));
if (element.getParent() != null) {
si.setContainerName(element.getParent().getElementName());
}
Expand Down Expand Up @@ -147,7 +153,7 @@ private DocumentSymbol toDocumentSymbol(IJavaElement unit, IProgressMonitor moni
symbol.setName(name);
symbol.setRange(getRange(unit));
symbol.setSelectionRange(getSelectionRange(unit));
symbol.setKind(mapKind(unit));
symbol.setKind(getSymbolKind(unit));
symbol.setDeprecated(isDeprecated(unit));
symbol.setDetail(getDetail(unit, name));
if (unit instanceof IParent) {
Expand All @@ -165,34 +171,6 @@ private DocumentSymbol toDocumentSymbol(IJavaElement unit, IProgressMonitor moni
return symbol;
}

private String getName(IJavaElement element) {
String name = JavaElementLabels.getElementLabel(element, ALL_DEFAULT);
return name == null ? element.getElementName() : name;
}

private Range getRange(IJavaElement element) throws JavaModelException {
return JDTUtils.toLocation(element, FULL_RANGE).getRange();
}

private Range getSelectionRange(IJavaElement element) throws JavaModelException {
return JDTUtils.toLocation(element).getRange();
}

private boolean isDeprecated(IJavaElement element) throws JavaModelException {
if (element instanceof ITypeRoot) {
return Flags.isDeprecated(((ITypeRoot) element).findPrimaryType().getFlags());
}
return false;
}

private String getDetail(IJavaElement element, String name) {
String nameWithDetails = JavaElementLabels.getElementLabel(element, ALL_DEFAULT | M_APP_RETURNTYPE | ROOT_VARIABLE);
if (nameWithDetails != null && nameWithDetails.startsWith(name)) {
return nameWithDetails.substring(name.length());
}
return "";
}

private IJavaElement[] filter(IJavaElement[] elements) {
return Stream.of(elements)
.filter(e -> (!isInitializer(e) && !isSyntheticElement(e)))
Expand Down Expand Up @@ -224,7 +202,119 @@ private boolean isSyntheticElement(IJavaElement element) {
}
}

public static SymbolKind mapKind(IJavaElement element) {
/**
* Returns with the human readable name of the element. For types with type
* arguments, it is {@code Comparable<T>} instead of {@code Comparable}. First,
* this method tries to retrieve the
* {@link JavaElementLabels#getElementLabel(IJavaElement, long) label} of the
* element, then falls back to {@link IJavaElement#getElementName() element
* name}. Returns {@code null} if the argument does not have a name.
*/
public static String getName(IJavaElement element) {
Assert.isNotNull(element, "element");
String name = JavaElementLabels.getElementLabel(element, ALL_DEFAULT);
return name == null ? element.getElementName() : name;
}

/**
* The location, encapsulating the range enclosing this symbol not including
* leading/trailing whitespace but everything else like comments.
*/
public static Location getFullLocation(IJavaElement element) throws JavaModelException {
Assert.isNotNull(element, "element");
return getLocation(element, FULL_RANGE);
}

/**
* The location, including the range that should be selected and revealed when
* this symbol is being picked, e.g the name of a method. Always contained by
* the range of the {@link #getFullLocation(IJavaElement) full location}.
*/
public static Location getSelectionLocation(IJavaElement element) throws JavaModelException {
Assert.isNotNull(element, "element");
return getLocation(element, NAME_RANGE);
}

/**
* Gets the location of the Java {@code element} based on the desired
* {@code locationType}.
*/
private static Location getLocation(IJavaElement element, LocationType locationType) throws JavaModelException {
Assert.isNotNull(element, "element");
Assert.isNotNull(locationType, "locationType");
Location location = locationType.toLocation(element);
if (location == null && element instanceof IType) {
IType type = (IType) element;
ICompilationUnit unit = (ICompilationUnit) type.getAncestor(COMPILATION_UNIT);
IClassFile classFile = (IClassFile) type.getAncestor(CLASS_FILE);
if (unit != null || (classFile != null && classFile.getSourceRange() != null)) {
return locationType.toLocation(type);
}
if (type instanceof IMember && ((IMember) type).getClassFile() != null) {
return JDTUtils.toLocation(((IMember) type).getClassFile());
}
}
return location;
}

/**
* The range enclosing this symbol not including leading/trailing whitespace but
* everything else like comments.
*
* @see DocumentSymbolHandler#getFullLocation(IJavaElement)
*/
public static Range getRange(IJavaElement element) throws JavaModelException {
Assert.isNotNull(element, "element");
return getLocation(element, FULL_RANGE).getRange();
}

/**
* The range that should be selected and revealed when this symbol is being
* picked, e.g the name of a method. Always contained by the range of the
* {@link #getFullLocation(IJavaElement) full location}.
*
* @see DocumentSymbolHandler#getSelectionLocation(IJavaElement)
*/
public static Range getSelectionRange(IJavaElement element) throws JavaModelException {
Assert.isNotNull(element, "element");
return getLocation(element, NAME_RANGE).getRange();
}

/**
* {@code true} if the element is deprecated. Otherwise, {@code false}.
*/
public static boolean isDeprecated(IJavaElement element) throws JavaModelException {
Assert.isNotNull(element, "element");
if (element instanceof ITypeRoot) {
return Flags.isDeprecated(((ITypeRoot) element).findPrimaryType().getFlags());
} else if (element instanceof IMember) {
return Flags.isDeprecated(((IMember) element).getFlags());
}
return false;
}

/**
* Returns with the details of the document symbol. This is usually the type
* information of a member. For methods, this is the type information of the
* return type. Can return with an empty string, but never {@code null}.
*
* @see DocumentSymbolHandler#getName(IJavaElement)
*/
public static String getDetail(IJavaElement element, String name) {
Assert.isNotNull(element, "element");
Assert.isNotNull(name, "name");
String nameWithDetails = JavaElementLabels.getElementLabel(element, ALL_DEFAULT | M_APP_RETURNTYPE | ROOT_VARIABLE);
if (nameWithDetails != null && nameWithDetails.startsWith(name)) {
return nameWithDetails.substring(name.length());
}
return "";
}

/**
* Returns with the document symbol {@code SymbolKind kind} for the Java
* element.
*/
public static SymbolKind getSymbolKind(IJavaElement element) {
switch (element.getElementType()) {
case IJavaElement.ANNOTATION:
return SymbolKind.Property; // TODO: find a better mapping
Expand All @@ -247,7 +337,14 @@ public static SymbolKind mapKind(IJavaElement element) {
return SymbolKind.Package;
case IJavaElement.TYPE:
try {
return (((IType)element).isInterface() ? SymbolKind.Interface : SymbolKind.Class);
IType type = (IType) element;
if (type.isEnum()) {
return SymbolKind.Enum;
} else if (type.isInterface()) {
return SymbolKind.Interface;
} else {
return SymbolKind.Class;
}
} catch (JavaModelException e) {
return SymbolKind.Class;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ InitializeResult initialize(InitializeParams param) {
wsCapabilities.setWorkspaceFolders(wsFoldersOptions);
capabilities.setWorkspace(wsCapabilities);

capabilities.setTypeHierarchy(true);

result.setCapabilities(capabilities);
return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,20 @@ public CompletableFuture<List<? extends Location>> implementation(TextDocumentPo
return computeAsyncWithClientProgress((monitor) -> new ImplementationsHandler(preferenceManager).findImplementations(position, monitor));
}

@Override
public CompletableFuture<DocumentSymbol> subTypes(TextDocumentPositionParams params) {
logInfo(">> textDocument/subTypes");
TypeHierarchyHandler handler = new TypeHierarchyHandler(preferenceManager);
return computeAsyncWithClientProgress(monitor -> handler.getSubTypes(params));
}

@Override
public CompletableFuture<DocumentSymbol> superTypes(TextDocumentPositionParams params) {
logInfo(">> textDocument/superTypes");
TypeHierarchyHandler handler = new TypeHierarchyHandler(preferenceManager);
return computeAsyncWithClientProgress(monitor -> handler.getSuperTypes(params));
}

public void sendStatus(ServiceStatus serverStatus, String status) {
if (client != null) {
client.sendStatus(serverStatus, status);
Expand Down
Loading

0 comments on commit decc6f6

Please sign in to comment.