package com.redhat.ceylon.eclipse.code.resolve;
import static com.redhat.ceylon.eclipse.code.editor.Navigation.gotoNode;
import static com.redhat.ceylon.eclipse.code.editor.Navigation.resolveNative;
import static com.redhat.ceylon.eclipse.util.Nodes.findNode;
import static com.redhat.ceylon.eclipse.util.Nodes.getIdentifyingNode;
import static com.redhat.ceylon.eclipse.util.Nodes.getReferencedModel;
import static com.redhat.ceylon.eclipse.util.Nodes.getReferencedNode;
import java.util.List;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.hyperlink.IHyperlink;
import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
import com.redhat.ceylon.common.Backends;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.eclipse.code.correct.CorrectionUtil;
import com.redhat.ceylon.eclipse.code.editor.CeylonEditor;
import com.redhat.ceylon.eclipse.code.parse.CeylonParseController;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.Referenceable;
import com.redhat.ceylon.model.typechecker.model.TypedDeclaration;
public class CeylonHyperlinkDetector implements IHyperlinkDetector {
private CeylonEditor editor;
private CeylonParseController controller;
public CeylonHyperlinkDetector(CeylonEditor editor,
CeylonParseController controller) {
this.editor = editor;
this.controller = controller;
}
private final class CeylonNodeLink implements IHyperlink {
private final Node node;
private final Node id;
private CeylonNodeLink(Node node, Node id) {
this.node = node;
this.id = id;
}
@Override
public void open() {
gotoNode(node, editor);
}
@Override
public String getTypeLabel() {
return null;
}
@Override
public String getHyperlinkText() {
Backends supportedBackends = supportedBackends();
String hint = CorrectionUtil.shortcut(
"com.redhat.ceylon.eclipse.ui.action.openSelectedDeclaration");
return "Declaration" +
(supportedBackends.none() ?
hint :
" \u2014 " +
(supportedBackends.header() ?
"native header" + hint :
supportedBackends +
" implementation"));
}
@Override
public IRegion getHyperlinkRegion() {
return new Region(id.getStartIndex(), id.getDistance());
}
}
@Override
public IHyperlink[] detectHyperlinks(ITextViewer textViewer,
IRegion region, boolean canShowMultipleHyperlinks) {
if (controller==null) {
return null;
}
Tree.CompilationUnit rootNode =
controller.getLastCompilationUnit();
if (rootNode==null) {
return null;
}
Backends supportedBackends = supportedBackends();
Node node =
findNode(rootNode,
controller.getTokens(),
region.getOffset(),
region.getOffset() +
region.getLength());
if (node==null) {
return null;
}
else if (node instanceof Tree.Declaration) {
boolean syntheticVar =
node instanceof Tree.Variable &&
((Tree.Variable) node).getType()
instanceof Tree.SyntheticVariable;
if (!syntheticVar) {
Tree.Declaration decNode =
(Tree.Declaration) node;
if (decNode.getDeclarationModel()
.getNativeBackends()
.equals(supportedBackends)) {
//we're already at the declaration itself
return null;
}
}
}
else if (node instanceof Tree.ImportPath) {
List<Tree.PackageDescriptor> packageDescriptors =
rootNode.getPackageDescriptors();
List<Tree.ModuleDescriptor> moduleDescriptors =
rootNode.getModuleDescriptors();
if (!packageDescriptors.isEmpty() &&
packageDescriptors.get(0)
.getImportPath()
== node
|| !moduleDescriptors.isEmpty() &&
moduleDescriptors.get(0)
.getImportPath()
== node) {
//we're already at the descriptor for
//the module or package
return null;
}
}
Node id = getIdentifyingNode(node);
if (id==null) {
return null;
}
Referenceable referenceable =
getReferencedModel(node);
if (referenceable==null) {
return null;
}
if (referenceable instanceof Declaration) {
Declaration dec =
(Declaration)
referenceable;
//look for the "original" declaration,
//ignoring narrowing synthetic declarations
if (dec instanceof TypedDeclaration) {
Declaration od = dec;
while (od!=null) {
referenceable = dec = od;
TypedDeclaration td =
(TypedDeclaration) od;
od = td.getOriginalDeclaration();
}
}
if (dec.isNative()) {
//for native declarations, each subclass of
//this hyperlink detector resolves to a
//different native header or impl
referenceable =
resolveNative(dec,
supportedBackends);
}
else {
//for other declarations, the subclasses of
//this hyperlink detector are disabled
if (!supportedBackends.none()) {
return null;
}
}
}
else {
//for module or package descriptors, the
//subclasses of this hyperlink detector are
//disabled
if (!supportedBackends.none()) {
return null;
}
}
Node r = getReferencedNode(referenceable);
if (r==null) {
return null;
}
else {
return new IHyperlink[] {
new CeylonNodeLink(r, id)
};
}
}
public Backends supportedBackends() {
return Backends.ANY;
}
}