package com.redhat.ceylon.eclipse.code.resolve;
import static com.redhat.ceylon.eclipse.code.editor.CeylonSourceViewer.SHOW_HIERARCHY;
import static com.redhat.ceylon.eclipse.code.editor.Navigation.gotoDeclaration;
import static com.redhat.ceylon.eclipse.util.EditorUtil.getPopupStyle;
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.model.typechecker.model.ModelUtil.isTypeUnknown;
import java.util.ArrayList;
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.compiler.typechecker.tree.Node;
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.eclipse.code.search.ReferencesPopup;
import com.redhat.ceylon.model.typechecker.model.Class;
import com.redhat.ceylon.model.typechecker.model.ClassOrInterface;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.Functional;
import com.redhat.ceylon.model.typechecker.model.ModelUtil;
import com.redhat.ceylon.model.typechecker.model.Referenceable;
import com.redhat.ceylon.model.typechecker.model.Type;
import com.redhat.ceylon.model.typechecker.model.TypeDeclaration;
import com.redhat.ceylon.model.typechecker.model.TypedDeclaration;
public class ReferencesHyperlinkDetector implements IHyperlinkDetector {
private static final IHyperlink[] NO_HYPERLINKS = new IHyperlink[0];
private CeylonEditor editor;
private CeylonParseController controller;
public ReferencesHyperlinkDetector(CeylonEditor editor,
CeylonParseController controller) {
this.editor = editor;
this.controller = controller;
}
private final class CeylonQuickReferencesLink implements IHyperlink {
private final Node id;
private CeylonQuickReferencesLink(Node id) {
this.id = id;
}
@Override
public void open() {
//TODO: if there is just one reference,
// navigate straight to it?
// editor.getCeylonSourceViewer()
// .doOperation(SHOW_REFERENCES);
ReferencesPopup popup =
new ReferencesPopup(
editor.getSite().getShell(),
getPopupStyle(), editor);
popup.show(false);
}
@Override
public String getTypeLabel() {
return null;
}
@Override
public String getHyperlinkText() {
return "References" +
CorrectionUtil.shortcut(
"com.redhat.ceylon.eclipse.ui.editor.findReferences");
}
@Override
public IRegion getHyperlinkRegion() {
return new Region(id.getStartIndex(),
id.getDistance());
}
}
private final class CeylonQuickRefinementsLink implements IHyperlink {
private final Node id;
private Declaration dec;
private CeylonQuickRefinementsLink(Node id, Declaration dec) {
this.id = id;
this.dec = dec;
}
@Override
public void open() {
//TODO: if there is just one refinement,
// navigate straight to it?
ReferencesPopup popup =
new ReferencesPopup(
editor.getSite().getShell(),
getPopupStyle(), editor);
popup.show(true);
}
@Override
public String getTypeLabel() {
return null;
}
@Override
public String getHyperlinkText() {
String hint =
CorrectionUtil.shortcut(
"com.redhat.ceylon.eclipse.ui.editor.findReferences");
if (!hint.isEmpty()) {
hint = hint + hint.substring(2);
}
String action =
dec instanceof TypeDeclaration ?
"Subtypes" : "Refinements";
return action + hint;
}
@Override
public IRegion getHyperlinkRegion() {
return new Region(id.getStartIndex(),
id.getDistance());
}
}
private final class CeylonQuickHierarchyLink implements IHyperlink {
private final Node id;
private CeylonQuickHierarchyLink(Node id) {
this.id = id;
}
@Override
public void open() {
editor.getCeylonSourceViewer()
.doOperation(SHOW_HIERARCHY);
}
@Override
public String getTypeLabel() {
return null;
}
@Override
public String getHyperlinkText() {
return "Hierarchy" +
CorrectionUtil.shortcut(
"com.redhat.ceylon.eclipse.ui.editor.hierarchy");
}
@Override
public IRegion getHyperlinkRegion() {
return new Region(id.getStartIndex(),
id.getDistance());
}
}
/*private final class CeylonReferencesLink implements IHyperlink {
private final Referenceable dec;
private final Node id;
private CeylonReferencesLink(Referenceable dec, Node id) {
this.dec = dec;
this.id = id;
}
@Override
public void open() {
new FindReferencesAction(editor, dec).run();
}
@Override
public String getTypeLabel() {
return null;
}
@Override
public String getHyperlinkText() {
return "Find References";
}
@Override
public IRegion getHyperlinkRegion() {
return new Region(id.getStartIndex(),
id.getDistance());
}
}*/
private final class CeylonRefinementLink implements IHyperlink {
private final Referenceable dec;
private final Node id;
private CeylonRefinementLink(Referenceable dec, Node id) {
this.dec = dec;
this.id = id;
}
@Override
public void open() {
gotoDeclaration(dec);
}
@Override
public String getTypeLabel() {
return null;
}
@Override
public String getHyperlinkText() {
return "Refined Declaration" +
CorrectionUtil.shortcut(
"com.redhat.ceylon.eclipse.ui.action.openRefinedDeclaration");
}
@Override
public IRegion getHyperlinkRegion() {
return new Region(id.getStartIndex(),
id.getDistance());
}
}
private final class CeylonSuperclassLink implements IHyperlink {
private final Referenceable dec;
private final Node id;
private CeylonSuperclassLink(Referenceable dec, Node id) {
this.dec = dec;
this.id = id;
}
@Override
public void open() {
gotoDeclaration(dec);
}
@Override
public String getTypeLabel() {
return null;
}
@Override
public String getHyperlinkText() {
return "Superclass";
}
@Override
public IRegion getHyperlinkRegion() {
return new Region(id.getStartIndex(),
id.getDistance());
}
}
private final class CeylonTypeLink implements IHyperlink {
private final Type type;
private final Node id;
private CeylonTypeLink(Type type, Node id) {
this.type = type;
this.id = id;
}
@Override
public void open() {
gotoDeclaration(type.getDeclaration());
}
@Override
public String getTypeLabel() {
return null;
}
@Override
public String getHyperlinkText() {
return "Declared Type";
}
@Override
public IRegion getHyperlinkRegion() {
return new Region(id.getStartIndex(),
id.getDistance());
}
}
/*private final class CeylonHierarchyLink implements IHyperlink {
private final Declaration dec;
private final Node id;
private CeylonHierarchyLink(Declaration dec, Node id) {
this.dec = dec;
this.id = id;
}
@Override
public void open() {
try {
showHierarchyView().focusOn(dec);
}
catch (PartInitException e) {
e.printStackTrace();
}
}
@Override
public String getTypeLabel() {
return null;
}
@Override
public String getHyperlinkText() {
return "Type Hierarchy View";
}
@Override
public IRegion getHyperlinkRegion() {
return new Region(id.getStartIndex(),
id.getDistance());
}
}*/
@Override
public IHyperlink[] detectHyperlinks(ITextViewer textViewer,
IRegion region, boolean canShowMultipleHyperlinks) {
if (controller==null ||
controller.getLastCompilationUnit()==null) {
return null;
}
else {
Node node =
findNode(controller.getLastCompilationUnit(),
controller.getTokens(),
region.getOffset(),
region.getOffset() +
region.getLength());
if (node==null) {
return null;
}
else {
Node id = getIdentifyingNode(node);
if (id==null) {
return null;
}
else {
Referenceable referenceable =
getReferencedModel(node);
if (referenceable!=null) {
if (referenceable instanceof Declaration) {
Declaration dec =
(Declaration)
referenceable;
List<IHyperlink> links = new ArrayList<>();
links.add(new CeylonQuickReferencesLink(id));
if (dec.isFormal() || dec.isDefault()
|| dec instanceof ClassOrInterface) {
links.add(new CeylonQuickRefinementsLink(id, dec));
}
if (dec.isActual()) {
Declaration refined =
dec.getRefinedDeclaration();
if (refined!=null) {
links.add(new CeylonRefinementLink(refined, id));
}
}
if (dec instanceof TypedDeclaration) {
boolean isVoid =
dec instanceof Functional &&
((Functional) dec).isDeclaredVoid();
TypedDeclaration td =
(TypedDeclaration) dec;
Type type = td.getType();
if (!isVoid &&
!isTypeUnknown(type)
&& !type.isUnion()
&& !type.isIntersection()) {
links.add(new CeylonTypeLink(type, id));
}
}
if (dec instanceof Class) {
Class c = (Class) dec;
Type extendedType = c.getExtendedType();
if (!ModelUtil.isTypeUnknown(extendedType)) {
links.add(new CeylonSuperclassLink(extendedType.getDeclaration(), id));
}
}
if (dec instanceof ClassOrInterface ||
dec.isActual() || dec.isFormal() || dec.isDefault()) {
links.add(new CeylonQuickHierarchyLink(id));
}
return links.toArray(NO_HYPERLINKS);
}
else {
return null;
}
}
else {
return null;
}
}
}
}
}
}