/
ScalaDeclarationHyperlinkComputer.scala
82 lines (71 loc) · 3.54 KB
/
ScalaDeclarationHyperlinkComputer.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package scala.tools.eclipse.hyperlink.text.detector
import org.eclipse.jface.text.IRegion
import org.eclipse.jface.text.hyperlink.IHyperlink
import scala.Option.option2Iterable
import scala.tools.eclipse.{ScalaPresentationCompiler => compiler}
import scala.tools.eclipse.javaelements.ScalaCompilationUnit
import scala.tools.eclipse.logging.HasLogger
import scala.tools.eclipse.hyperlink.text._
import scala.tools.eclipse.InteractiveCompilationUnit
import scala.tools.eclipse.ScalaPresentationCompiler
class ScalaDeclarationHyperlinkComputer extends HasLogger {
def findHyperlinks(icu: InteractiveCompilationUnit, wordRegion: IRegion): Option[List[IHyperlink]] = {
findHyperlinks(icu, wordRegion, wordRegion)
}
def findHyperlinks(icu: InteractiveCompilationUnit, wordRegion: IRegion, mappedRegion: IRegion): Option[List[IHyperlink]] = {
logger.info("detectHyperlinks: wordRegion = " + mappedRegion)
/** In 2.9 ClazzTag was called ClassTag. Source-compatibility hack */
implicit def compatClazzTag(com: ScalaPresentationCompiler) = new {
def ClazzTag = 12 // we really, really hope this constant won't change in 2.9.x
}
icu.withSourceFile({ (sourceFile, compiler) =>
object DeclarationHyperlinkFactory extends HyperlinkFactory {
protected val global: compiler.type = compiler
}
if (mappedRegion == null || mappedRegion.getLength == 0)
None
else {
val start = mappedRegion.getOffset
val regionEnd = mappedRegion.getOffset + mappedRegion.getLength
// removing 1 handles correctly hyperlinking requests @ EOF
val end = if (sourceFile.length == regionEnd) regionEnd - 1 else regionEnd
val pos = compiler.rangePos(sourceFile, start, start, end)
import compiler.{ log => _, _ }
val response = new Response[compiler.Tree]
askTypeAt(pos, response)
val symsOpt = response.get.left.toOption.map { tree =>
compiler.askOption { () =>
tree match {
case Import(expr, sels) =>
if (expr.pos.includes(pos)) {
@annotation.tailrec
def locate(p: Position, inExpr: Tree): Symbol = inExpr match {
case Select(qualifier, name) =>
if (qualifier.pos.includes(p)) locate(p, qualifier)
else inExpr.symbol
case tree => tree.symbol
}
List(locate(pos, expr))
} else {
sels find (selPos => selPos.namePos >= pos.start && selPos.namePos <= pos.end) map { sel =>
val tpe = stabilizedType(expr)
List(tpe.member(sel.name), tpe.member(sel.name.toTypeName))
} getOrElse Nil
}
case Annotated(atp, _) => List(atp.symbol)
case Literal(const) if const.tag == compiler.ClazzTag => List(const.typeValue.typeSymbol)
case ap @ Select(qual, nme.apply) => List(ap.symbol, qual.symbol)
case st if st.symbol ne null => List(st.symbol)
case _ => List()
}
}
}.flatten
symsOpt map { syms =>
syms filterNot { sym => sym == NoSymbol || sym.isPackage || sym.isJavaDefined } flatMap { sym =>
DeclarationHyperlinkFactory.create(Hyperlink.withText("Open Declaration (%s)".format(sym.toString)), sym, wordRegion)
}
}
}
})(None)
}
}