forked from eclipse-jdtls/eclipse.jdt.ls
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
eclipse-jdtlsGH-28: !!!FRAGMENT!!! Added support for the type hierarchy.
Closes: eclipse-jdtls#28. Signed-off-by: Akos Kitta <[email protected]>
- Loading branch information
Akos Kitta
committed
Oct 5, 2018
1 parent
b5954df
commit 43ec377
Showing
3 changed files
with
389 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
293 changes: 293 additions & 0 deletions
293
...lipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/TypeHierarchyHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,293 @@ | ||
/******************************************************************************* | ||
* Copyright (c) 2018 TypeFox 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: | ||
* TypeFox - initial API and implementation | ||
*******************************************************************************/ | ||
package org.eclipse.jdt.ls.core.internal.handlers; | ||
|
||
import static com.google.common.collect.Lists.newArrayList; | ||
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.LOCAL_VARIABLE; | ||
import static org.eclipse.jdt.core.IJavaElement.METHOD; | ||
import static org.eclipse.jdt.core.IJavaElement.TYPE; | ||
import static org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin.logException; | ||
import static org.eclipse.jdt.ls.core.internal.handlers.DocumentSymbolHandler.mapKind; | ||
import static org.eclipse.jdt.ls.core.internal.hover.JavaElementLabels.ALL_DEFAULT; | ||
import static org.eclipse.jdt.ls.core.internal.hover.JavaElementLabels.DEFAULT_QUALIFIED; | ||
|
||
import java.util.Map; | ||
import java.util.function.Function; | ||
import java.util.stream.Stream; | ||
|
||
import org.eclipse.core.runtime.IProgressMonitor; | ||
import org.eclipse.core.runtime.NullProgressMonitor; | ||
import org.eclipse.core.runtime.SubMonitor; | ||
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.ILocalVariable; | ||
import org.eclipse.jdt.core.IMember; | ||
import org.eclipse.jdt.core.IMethod; | ||
import org.eclipse.jdt.core.IPackageFragment; | ||
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.ls.core.internal.JDTUtils; | ||
import org.eclipse.jdt.ls.core.internal.hover.JavaElementLabels; | ||
import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager; | ||
import org.eclipse.lsp4j.Location; | ||
import org.eclipse.lsp4j.TextDocumentPositionParams; | ||
import org.eclipse.lsp4j.TypeHierarchyNode; | ||
import org.eclipse.xtext.xbase.lib.Exceptions; | ||
|
||
import com.google.common.collect.ImmutableMap; | ||
|
||
/** | ||
* Handler for serving the {@code textDocument/superTypes} and the | ||
* {@code textDocument/subTypes} methods. | ||
* | ||
* Calculates super- and subtype hierarchical information based on text document | ||
* positions. | ||
* | ||
*/ | ||
public class TypeHierarchyHandler { | ||
|
||
//@formatter:off | ||
protected static Map<Integer, Function<IJavaElement, IType>> SUPPORTED_TYPES = ImmutableMap.<Integer, Function<IJavaElement, IType>>builder() | ||
.put(TYPE, type -> (IType) type) | ||
.put(METHOD, method -> ((IMethod) method).getDeclaringType()) | ||
.put(LOCAL_VARIABLE, variable -> ((ILocalVariable) variable).getDeclaringMember().getDeclaringType()) | ||
.build(); | ||
//@formatter:on | ||
|
||
private PreferenceManager preferenceManager; | ||
|
||
public TypeHierarchyHandler(PreferenceManager preferenceManager) { | ||
this.preferenceManager = preferenceManager; | ||
} | ||
|
||
public TypeHierarchyNode getSubTypes(TextDocumentPositionParams params) { | ||
return getSubTypes(params, new NullProgressMonitor()); | ||
} | ||
|
||
public TypeHierarchyNode getSubTypes(TextDocumentPositionParams params, IProgressMonitor monitor) { | ||
return getTypeHierarchy(params, TypeHierarchyStrategy.SUB, monitor); | ||
} | ||
|
||
public TypeHierarchyNode getSuperTypes(TextDocumentPositionParams params) { | ||
return getSuperTypes(params, new NullProgressMonitor()); | ||
} | ||
|
||
public TypeHierarchyNode getSuperTypes(TextDocumentPositionParams params, IProgressMonitor monitor) { | ||
return getTypeHierarchy(params, TypeHierarchyStrategy.SUPER, monitor); | ||
} | ||
|
||
protected TypeHierarchyNode getTypeHierarchy(TextDocumentPositionParams params, TypeHierarchyStrategy strategy, IProgressMonitor monitor) { | ||
SubMonitor subMonitor = SubMonitor.convert(monitor, 3); | ||
String uri = params.getTextDocument().getUri(); | ||
ITypeRoot root = JDTUtils.resolveTypeRoot(uri); | ||
if (root == null) { | ||
return null; | ||
} | ||
try { | ||
if (root instanceof ICompilationUnit) { | ||
ICompilationUnit unit = (ICompilationUnit) root; | ||
if (root.getResource() == null || root.getResource().isDerived()) { | ||
return null; | ||
} | ||
reconcile(unit, subMonitor.newChild(1)); | ||
} | ||
|
||
int line = params.getPosition().getLine(); | ||
int character = params.getPosition().getCharacter(); | ||
IJavaElement selectedElement = JDTUtils.findElementAtSelection(root, line, character, preferenceManager, subMonitor.newChild(1)); | ||
if (selectedElement == null) { | ||
return null; | ||
} | ||
|
||
if (isSupportedType(selectedElement)) { | ||
IType selectedType = getType(selectedElement); | ||
if (selectedType != null) { | ||
ITypeHierarchy hierarchy = strategy.getTypeHierarchy(selectedType, subMonitor.newChild(1)); | ||
return mapToNode(hierarchy, strategy); | ||
} | ||
} | ||
} catch (JavaModelException e) { | ||
logException("Error when loading type hierarchy for " + uri + ".", e); | ||
} | ||
return null; | ||
} | ||
|
||
private void reconcile(ICompilationUnit unit, IProgressMonitor monitor) throws JavaModelException { | ||
unit.reconcile(ICompilationUnit.NO_AST, false, null, monitor); | ||
} | ||
|
||
private boolean isSupportedType(IJavaElement element) { | ||
return SUPPORTED_TYPES.keySet().contains(element.getElementType()); | ||
} | ||
|
||
private IType getType(IJavaElement element) { | ||
Function<IJavaElement, IType> function = SUPPORTED_TYPES.get(element.getElementType()); | ||
if (function != null) { | ||
return function.apply(element); | ||
} | ||
return null; | ||
} | ||
|
||
private TypeHierarchyNode mapToNode(ITypeHierarchy hierarchy, TypeHierarchyStrategy strategy) throws JavaModelException { | ||
if (hierarchy == null) { | ||
return null; | ||
} | ||
IType type = hierarchy.getType(); | ||
TypeHierarchyNode node = mapToNode(type); | ||
if (isValid(node)) { | ||
//@formatter:off | ||
node.setChildren(Stream.of(strategy.getChildren(type, hierarchy)) | ||
.map(childType -> mapToNode(childType)) | ||
.filter(childNode -> isValid(childNode)) | ||
.collect(toList())); | ||
//@formatter:on | ||
} | ||
return node; | ||
} | ||
|
||
/** | ||
* Maps the type argument into the corresponding hierarchy node. Returns with | ||
* {@code null} if the type is not supported. The | ||
* {@link TypeHierarchyNode#getChildren() children} will be initialized, but | ||
* will be empty after calling this method. | ||
* | ||
* <p> | ||
* <b>NOTE:</b>it throws a {@link JavaModelException}. | ||
*/ | ||
private TypeHierarchyNode mapToNode(IType type) { | ||
if (!isSupportedType(type)) { | ||
return null; | ||
} | ||
try { | ||
//@formatter:off | ||
return new TypeHierarchyNode( | ||
getName(type), | ||
mapKind(type), | ||
getLocation(type), | ||
isDeprecated(type), | ||
/* getDetail(type), */ /*TODO this should go the the type*/ | ||
newArrayList()); | ||
//@formatter:on | ||
} catch (JavaModelException e) { | ||
Exceptions.sneakyThrow(e); | ||
} | ||
return null; | ||
} | ||
|
||
/** | ||
* The short name of the type. | ||
*/ | ||
private String getName(IType type) { | ||
String name = JavaElementLabels.getElementLabel(type, ALL_DEFAULT); | ||
return name == null ? type.getElementName() : name; | ||
} | ||
|
||
private Location getLocation(IType type) throws JavaModelException { | ||
ICompilationUnit unit = (ICompilationUnit) type.getAncestor(COMPILATION_UNIT); | ||
IClassFile classFile = (IClassFile) type.getAncestor(CLASS_FILE); | ||
if (unit != null || (classFile != null && classFile.getSourceRange() != null)) { | ||
return JDTUtils.toLocation(type); | ||
} | ||
if (type instanceof IMember && ((IMember) type).getClassFile() != null) { | ||
return JDTUtils.toLocation(((IMember) type).getClassFile()); | ||
} | ||
return JDTUtils.toLocation(type); | ||
} | ||
|
||
private boolean isDeprecated(IType type) throws JavaModelException { | ||
return Flags.isDeprecated(type.getFlags()); | ||
} | ||
|
||
/** | ||
* The name of the container package. | ||
*/ | ||
@SuppressWarnings("unused") | ||
private String getDetail(IType type) { | ||
IPackageFragment packageFragment = type.getPackageFragment(); | ||
if (packageFragment != null) { | ||
String name = JavaElementLabels.getElementLabel(packageFragment, ALL_DEFAULT); | ||
return name == null ? packageFragment.getElementName() : name; | ||
} | ||
String fqnName = JavaElementLabels.getElementLabel(type, DEFAULT_QUALIFIED); | ||
if (fqnName != null) { | ||
String name = getName(type); | ||
if (name != null && fqnName.endsWith(name)) { | ||
return fqnName.substring(0, fqnName.length() - (name.length() + 1)); | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
// https://github.com/eclipse/lsp4j/issues/264 | ||
private boolean isValid(TypeHierarchyNode node) { | ||
return node != null && node.getName() != null && node.getLocation() != null && node.getKind() != null; | ||
} | ||
|
||
/** | ||
* Strategies for collecting super- and subtypes. | ||
*/ | ||
protected static enum TypeHierarchyStrategy { | ||
|
||
SUB { | ||
|
||
@Override | ||
protected ITypeHierarchy getTypeHierarchy(IType type, IProgressMonitor monitor) throws JavaModelException { | ||
return type.newTypeHierarchy(monitor); | ||
} | ||
|
||
@Override | ||
protected IType[] getChildren(IType type, ITypeHierarchy hierarchy) throws JavaModelException { | ||
IType[] children = hierarchy.getSubtypes(type); | ||
return children == null ? EMPTY : children; | ||
} | ||
|
||
}, | ||
|
||
SUPER { | ||
|
||
@Override | ||
protected ITypeHierarchy getTypeHierarchy(IType type, IProgressMonitor monitor) throws JavaModelException { | ||
return type.newSupertypeHierarchy(monitor); | ||
} | ||
|
||
@Override | ||
protected IType[] getChildren(IType type, ITypeHierarchy hierarchy) throws JavaModelException { | ||
IType[] children = hierarchy.getSupertypes(type); | ||
return children == null ? EMPTY : children; | ||
} | ||
|
||
}; | ||
|
||
/** | ||
* Shared empty types. | ||
*/ | ||
private static final IType[] EMPTY = new IType[0]; | ||
|
||
/** | ||
* Returns with the type hierarchy for the given type argument. | ||
*/ | ||
protected abstract ITypeHierarchy getTypeHierarchy(IType type, IProgressMonitor monitor) throws JavaModelException; | ||
|
||
/** | ||
* Returns with the children (super-, or subtype) of the type from the | ||
* hierarchy. Never {@code null}. | ||
*/ | ||
protected abstract IType[] getChildren(IType type, ITypeHierarchy hierarchy) throws JavaModelException; | ||
} | ||
|
||
} |
Oops, something went wrong.