[bug 489745][callHierarchy] Callee Hierarchy Support

Change-Id: If9d2a4b733f3ad6823b927ae556c93b1dd167eac
Signed-off-by: akosyakov <anton.kosyakov@typefox.io>
This commit is contained in:
akosyakov 2016-04-17 08:14:56 +02:00
parent f1cd5f07d7
commit cd7ce88234
10 changed files with 158 additions and 62 deletions

View file

@ -9,13 +9,21 @@ package org.eclipse.xtext.ide.editor.hierarchy
import com.google.inject.Inject
import javax.inject.Provider
import org.eclipse.emf.common.util.URI
import org.eclipse.emf.ecore.EClass
import org.eclipse.emf.ecore.EClassifier
import org.eclipse.emf.ecore.EObject
import org.eclipse.xtend.lib.annotations.Accessors
import org.eclipse.xtext.findReferences.IReferenceFinder
import org.eclipse.xtext.findReferences.IReferenceFinder.IResourceAccess
import org.eclipse.xtext.findReferences.TargetURICollector
import org.eclipse.xtext.findReferences.TargetURIs
import org.eclipse.xtext.resource.IEObjectDescription
import org.eclipse.xtext.resource.IResourceDescriptions
import org.eclipse.xtext.resource.IResourceServiceProvider
import org.eclipse.xtext.util.concurrent.IUnitOfWork
import static extension org.eclipse.xtext.EcoreUtil2.*
/**
* @author kosyakov - Initial contribution and API
@ -42,5 +50,30 @@ abstract class AbstractHierarchyBuilder implements HierarchyBuilder {
@Inject
IResourceServiceProvider.Registry resourceServiceProviderRegistry
protected def <R> R readOnly(URI objectURI, IUnitOfWork<R, EObject> work) {
return getResourceAccess.readOnly(objectURI) [ resourceSet |
val targetObject = resourceSet.getEObject(objectURI, true)
return work.exec(targetObject)
]
}
protected def IEObjectDescription getDescription(URI objectURI) {
val resourceDescription = getIndexData.getResourceDescription(objectURI.trimFragment)
if(resourceDescription === null) return null
return resourceDescription.exportedObjects.findFirst[EObjectURI == objectURI]
}
protected def IEObjectDescription getDescription(EObject object) {
if(object === null) return null
return getIndexData.getExportedObjectsByObject(object).head
}
protected def isAssignable(EClass superType, EClassifier type) {
if (type instanceof EClass)
return superType.isAssignableFrom(type)
return false
}
}

View file

@ -0,0 +1,27 @@
/*******************************************************************************
* Copyright (c) 2016 TypeFox GmbH (http://www.typefox.io) 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
*******************************************************************************/
package org.eclipse.xtext.ide.editor.hierarchy
/**
* It is used to build a call hierarchy structure.
*
* @author kosyakov - Initial contribution and API
* @since 2.10
*/
interface CallHierarchyBuilder extends HierarchyBuilder {
static enum CallHierarchyType {
CALLER,
CALLEE
}
def CallHierarchyType getHierarchyType();
def void setHierarchyType(CallHierarchyType hierarchyType);
}

View file

@ -7,9 +7,12 @@
*******************************************************************************/
package org.eclipse.xtext.ide.editor.hierarchy
import java.util.Map
import org.eclipse.core.runtime.IProgressMonitor
import org.eclipse.emf.common.util.URI
import org.eclipse.emf.ecore.EObject
import org.eclipse.emf.ecore.EReference
import org.eclipse.xtend.lib.annotations.Accessors
import org.eclipse.xtext.findReferences.ReferenceAcceptor
import org.eclipse.xtext.findReferences.TargetURIs
import org.eclipse.xtext.resource.IEObjectDescription
@ -27,47 +30,80 @@ import static extension org.eclipse.xtext.nodemodel.util.NodeModelUtils.*
* @author kosyakov - Initial contribution and API
* @since 2.10
*/
class DefaultCallHierarchyBuilder extends AbstractHierarchyBuilder {
class DefaultCallHierarchyBuilder extends AbstractHierarchyBuilder implements CallHierarchyBuilder {
override buildRoots(URI rootURI, IProgressMonitor progressMonitor) {
val rootDeclaration = rootURI.rootDeclaration
@Accessors
CallHierarchyType hierarchyType = CallHierarchyType.CALLER
override buildRoots(URI rootURI, IProgressMonitor monitor) {
val rootDeclaration = rootURI.findDeclaration
if(rootDeclaration === null) return emptyList
return #[rootDeclaration.createRoot]
}
override buildChildren(HierarchyNode parent, IProgressMonitor progressMonitor) {
override buildChildren(HierarchyNode parent, IProgressMonitor monitor) {
if (!parent.mayHaveChildren)
return emptyList
val children = newLinkedHashMap
parent.element.EObjectURI.findDeclarations(progressMonitor) [ declaration, reference |
var childNode = children.get(declaration.EObjectURI)
if (childNode === null) {
childNode = createChild(declaration, parent)
children.put(declaration.EObjectURI, childNode)
}
childNode.locations += reference.createLocation
findDeclarations(parent, monitor) [ declaration, reference |
val childNode = children.createChild(declaration, parent)
if (childNode !== null)
childNode.references += reference.createNodeReference
]
return children.values
}
protected def void findDeclarations(
URI targetURI,
IProgressMonitor progressMonitor,
HierarchyNode parent,
IProgressMonitor monitor,
(IEObjectDescription, IReferenceDescription)=>void acceptor
) {
val targetURIs = targetURI.collectTargetURIs
switch hierarchyType {
case CALLEE:
findTargetDeclarations(parent.element.EObjectURI, monitor, acceptor)
default:
findSourceDeclarations(parent.element.EObjectURI, monitor, acceptor)
}
}
protected def void findTargetDeclarations(
URI sourceDeclarationURI,
IProgressMonitor monitor,
(IEObjectDescription, IReferenceDescription)=>void acceptor
) {
readOnly(sourceDeclarationURI) [ sourceDeclaration |
referenceFinder.findAllReferences(
sourceDeclaration,
new ReferenceAcceptor(resourceServiceProviderRegistry) [ reference |
if (reference.filterReference) {
val targetDeclaration = reference?.findTargetDeclaration
acceptor.apply(targetDeclaration, reference)
}
],
monitor
)
null
]
}
protected def void findSourceDeclarations(
URI targetDeclarationURI,
IProgressMonitor monitor,
(IEObjectDescription, IReferenceDescription)=>void acceptor
) {
val targetURIs = targetDeclarationURI.collectTargetURIs
referenceFinder.findAllReferences(
targetURIs,
resourceAccess,
indexData,
new ReferenceAcceptor(resourceServiceProviderRegistry) [ reference |
val declaration = reference.declaration
if (declaration !== null)
acceptor.apply(declaration, reference)
if (reference.filterReference) {
val sourceDeclaration = reference?.findSourceDeclaration
acceptor.apply(sourceDeclaration, reference)
}
],
progressMonitor
monitor
)
}
@ -75,41 +111,27 @@ class DefaultCallHierarchyBuilder extends AbstractHierarchyBuilder {
val targetURIs = targetURIProvider.get
if(targetURI === null) return targetURIs
return resourceAccess.readOnly(targetURI) [ resourceSet |
val targetObject = resourceSet.getEObject(targetURI, true)
return readOnly(targetURI) [ targetObject |
if(targetObject === null) return targetURIs
targetURICollector.add(targetObject, targetURIs)
return targetURIs
]
}
protected def IEObjectDescription getRootDeclaration(URI rootURI) {
return rootURI.declaration.rootDeclaration
protected def boolean filterReference(IReferenceDescription reference) {
reference !== null
}
/**
* @returns a declaration representing a root hierarchy node for the given element; can return <code>null<code> if the hierarchy does not support such kind of declarations
*/
protected def IEObjectDescription getRootDeclaration(IEObjectDescription declaration) {
return declaration
protected def IEObjectDescription findDeclaration(URI objectURI) {
return objectURI.description
}
/**
* @returns a declaration representing a child node that can be reached with the given reference; can return <code>null</code> if the hierarchy does not support such kind of references
*/
protected def IEObjectDescription getDeclaration(IReferenceDescription reference) {
if(reference === null) return null
val declarationURI = reference.containerEObjectURI ?: reference.sourceEObjectUri
return declarationURI.declaration
protected def IEObjectDescription findTargetDeclaration(IReferenceDescription reference) {
return reference.targetEObjectUri.findDeclaration
}
protected def IEObjectDescription getDeclaration(URI declarationURI) {
val resourceDescription = indexData.getResourceDescription(declarationURI.trimFragment)
if(resourceDescription === null) return null
return resourceDescription.exportedObjects.findFirst[EObjectURI == declarationURI]
protected def IEObjectDescription findSourceDeclaration(IReferenceDescription reference) {
return reference.containerEObjectURI.findDeclaration
}
/**
@ -133,20 +155,34 @@ class DefaultCallHierarchyBuilder extends AbstractHierarchyBuilder {
return node
}
protected def HierarchyNode createChild(
Map<URI, HierarchyNode> children,
IEObjectDescription declaration,
HierarchyNode parent
) {
if(declaration === null) return null;
var childNode = children.get(declaration.EObjectURI)
if (childNode === null) {
childNode = createChild(declaration, parent)
children.put(declaration.EObjectURI, childNode)
}
return childNode
}
/**
* @returns a location for the given reference; cannot be <code>null</code>
* @returns a hierarchy node reference for the given reference; cannot be <code>null</code>
*/
protected def HierarchyNodeLocation createLocation(IReferenceDescription reference) {
return resourceAccess.readOnly(reference.sourceEObjectUri) [ resourceSet |
val obj = resourceSet.getEObject(reference.sourceEObjectUri, true)
val textRegion = obj.getTextRegion(reference)
val text = obj.getText(textRegion)
return new DefaultHierarchyNodeLocation(text, textRegion, reference)
protected def HierarchyNodeReference createNodeReference(IReferenceDescription reference) {
return readOnly(reference.sourceEObjectUri) [ sourceObject |
val textRegion = sourceObject.getTextRegion(reference.EReference, reference.indexInList)
val text = sourceObject.getText(textRegion)
return new DefaultHierarchyNodeReference(text, textRegion, reference)
]
}
protected def ITextRegionWithLineInformation getTextRegion(EObject obj, IReferenceDescription reference) {
return hierarchyNodeLocationProvider.getTextRegion(obj, reference.EReference, reference.indexInList)
protected def ITextRegionWithLineInformation getTextRegion(EObject obj, EReference reference, int indexInList) {
return hierarchyNodeLocationProvider.getTextRegion(obj, reference, indexInList)
}
protected def String getText(EObject obj, ITextRegionWithLineInformation textRegion) {

View file

@ -27,12 +27,12 @@ class DefaultHierarchyNode implements HierarchyNode {
IEObjectDescription element
@Accessors(PUBLIC_GETTER)
val locations = <HierarchyNodeLocation>newArrayList
val references = <HierarchyNodeReference>newArrayList
Wrapper<Boolean> recursive
override getNavigationElement() {
return locations.head ?: element
return references.head ?: element
}
override boolean isRecursive() {

View file

@ -18,7 +18,7 @@ import org.eclipse.xtext.util.ITextRegionWithLineInformation
*/
@Accessors
@FinalFieldsConstructor
class DefaultHierarchyNodeLocation implements HierarchyNodeLocation {
class DefaultHierarchyNodeReference implements HierarchyNodeReference {
val String text
@Delegate
val ITextRegionWithLineInformation textRegion

View file

@ -30,9 +30,9 @@ interface HierarchyNode extends Navigatable {
def HierarchyNode getParent()
/**
* @returns locations used to reach the node from a parent; empty if the node is a root
* @returns references used to reach the node from a parent; empty if the node is a root
*/
def Collection<HierarchyNodeLocation> getLocations()
def Collection<HierarchyNodeReference> getReferences()
/**
* @returns whether there is a parent (can be transitive) containing the same element as the node

View file

@ -13,7 +13,8 @@ import org.eclipse.emf.ecore.EStructuralFeature
import org.eclipse.xtext.util.ITextRegionWithLineInformation
/**
* This class is used to identify a region for {@link HierarchyNode} and {@link HierarchyNodeLocation}.
* This class is used to identify a region for {@link HierarchyNode} and {@link HierarchyNodeReference}.
*
* @author kosyakov - Initial contribution and API
* @since 2.10
*/

View file

@ -11,11 +11,11 @@ import org.eclipse.xtext.ide.editor.navigation.Navigatable
import org.eclipse.xtext.util.ITextRegionWithLineInformation
/**
* Represents a reference between parent and child nodes. Each location is backed up with a region and a text.
* Represents a reference between parent and child nodes. Each reference is backed up with a region and a text.
*
* @author kosyakov - Initial contribution and API
* @since 2.10
*/
interface HierarchyNodeLocation extends Navigatable, ITextRegionWithLineInformation {
interface HierarchyNodeReference extends Navigatable, ITextRegionWithLineInformation {
def String getText()
}

View file

@ -16,7 +16,6 @@ import org.eclipse.xtend.lib.annotations.Accessors
import org.eclipse.xtext.ide.editor.hierarchy.AbstractHierarchyBuilder
import org.eclipse.xtext.ide.editor.hierarchy.HierarchyBuilder
import org.eclipse.xtext.ide.editor.hierarchy.HierarchyNode
import org.eclipse.xtext.ide.editor.hierarchy.HierarchyNodeLocation
import org.eclipse.xtext.junit4.validation.ValidationTestHelper
import org.eclipse.xtext.resource.EObjectAtOffsetHelper
import org.eclipse.xtext.resource.IResourceDescriptionsProvider
@ -28,6 +27,7 @@ import org.eclipse.xtext.util.LazyStringInputStream
import static org.junit.Assert.*
import static extension org.eclipse.xtext.EcoreUtil2.*
import org.eclipse.xtext.ide.editor.hierarchy.HierarchyNodeReference
/**
* @author kosyakov - Initial contribution and API
@ -93,7 +93,7 @@ abstract class AbstractHierarchyBuilderTest {
'''
protected def String internalToExpectation(HierarchyNode node, HierarchyBuilder builder) '''
«FOR location : node.location
«FOR location : node.reference
«location.toExpectation»
«ENDFOR»
«IF node.mayHaveChildren»
@ -103,7 +103,7 @@ abstract class AbstractHierarchyBuilderTest {
«ENDIF»
'''
protected def String toExpectation(HierarchyNodeLocation location) {
protected def String toExpectation(HierarchyNodeReference location) {
''''«location.text»' [«location.offset», «location.length»]'''
}

View file

@ -27,7 +27,6 @@ import com.google.inject.Singleton;
* targets.
*
* @author Sebastian Zarnekow - Initial contribution and API
*
* @since 2.10
*/
@Singleton