/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package ca.weblite.netbeans.mirah.hyperlinks; import ca.weblite.netbeans.mirah.cc.MirahCodeCompleter; import ca.weblite.netbeans.mirah.lexer.MirahLanguageHierarchy; import ca.weblite.netbeans.mirah.lexer.MirahParser; import ca.weblite.netbeans.mirah.lexer.MirahTokenId; import ca.weblite.netbeans.mirah.lexer.SourceQuery; import java.util.EnumSet; import java.util.Set; import javax.swing.text.Document; import mirah.impl.Tokens; import mirah.lang.ast.FieldDeclaration; import mirah.lang.ast.Node; import org.mirah.typer.ResolvedType; import org.netbeans.api.editor.mimelookup.MimeRegistration; import org.netbeans.api.lexer.Token; import org.netbeans.api.lexer.TokenHierarchy; import org.netbeans.api.lexer.TokenSequence; import org.netbeans.editor.BaseDocument; import org.netbeans.lib.editor.hyperlink.spi.HyperlinkProviderExt; import org.netbeans.lib.editor.hyperlink.spi.HyperlinkType; /** * * @author shannah */ @MimeRegistration(mimeType = "text/x-mirah", service = HyperlinkProviderExt.class) public class MirahHyperlinkProvider implements HyperlinkProviderExt { private enum LinkType { Constant, Identifier, InstanceVar, ClassVar } private int spanStart = -1; private int spanEnd = -1; private LinkType linkType; private String target; @Override public Set<HyperlinkType> getSupportedHyperlinkTypes() { return EnumSet.of(HyperlinkType.GO_TO_DECLARATION); } @Override public boolean isHyperlinkPoint(Document doc, int offset, HyperlinkType type) { return getHyperlinkSpan(doc, offset, type) != null; } @Override public int[] getHyperlinkSpan(Document dcmnt, int i, HyperlinkType ht) { if ( verifyState(dcmnt, i)){ return new int[]{spanStart, spanEnd}; } else { return null; } } @Override public void performClickAction(Document dcmnt, int i, HyperlinkType ht) { } @Override public String getTooltipText(Document dcmnt, int i, HyperlinkType ht) { SourceQuery sq = new SourceQuery(dcmnt); if ( verifyState(dcmnt, i)){ String astResult = getTypeFromAST(dcmnt, spanStart, spanEnd); if ( astResult != null ){ return astResult; } switch ( linkType ){ case Constant: return sq.getFQN(target, i); case InstanceVar: sq = sq.findClass(i); for ( Node n : sq.findFieldDefinitions(target)){ FieldDeclaration fd = (FieldDeclaration)n; if ( fd.type() != null ){ return fd.type().typeref().name(); } } break; case Identifier: sq = sq.findMethod(i); sq = sq.findLocalVars(target); String type = sq.getType(); if ( type != null ){ return type; } break; } } return null; } public void resetState(){ spanStart = -1; spanEnd = -1; linkType = null; target = null; } public boolean verifyState(Document doc, int offset) { resetState(); BaseDocument bdoc = (BaseDocument)doc; bdoc.readLock(); try { TokenHierarchy hi = TokenHierarchy.get(doc); TokenSequence<MirahTokenId> ts = hi.tokenSequence(MirahTokenId.getLanguage()); if (ts != null) { ts.move(offset); ts.moveNext(); Token<MirahTokenId> tok = ts.token(); if ( tok.id().ordinal() == Tokens.tCONSTANT.ordinal()){ linkType = LinkType.Constant; } else if ( tok.id().ordinal() == Tokens.tIDENTIFIER.ordinal()){ linkType = LinkType.Identifier; } else if ( tok.id().ordinal() == MirahLanguageHierarchy.TYPE_HINT){ linkType = LinkType.Constant; } else if ( tok.id().ordinal() == Tokens.tInstVar.ordinal()){ linkType = LinkType.InstanceVar; } else if ( tok.id().ordinal() == Tokens.tClassVar.ordinal()){ linkType = LinkType.ClassVar; } if ( linkType != null ){ spanStart = tok.offset(hi); target = tok.text().toString(); spanEnd = spanStart+target.length(); return true; } } return false; } finally { bdoc.readUnlock(); } } private String getTypeFromAST(final Document doc, final int spanStart, final int spanEnd) { MirahParser.DocumentDebugger dbg = MirahParser.getDocumentDebugger(doc); if ( dbg != null ){ ResolvedType type = null; Node foundNode = MirahCodeCompleter.findNode(dbg, spanEnd); if ( foundNode != null ){ type = dbg.getType(foundNode); } if (type != null ){ return type.name(); } } return null; } }