/
BuildTargetClasses.scala
117 lines (102 loc) Β· 3.55 KB
/
BuildTargetClasses.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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package scala.meta.internal.metals
import ch.epfl.scala.{bsp4j => b}
import scala.collection.concurrent.TrieMap
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
import scala.meta.internal.metals.BuildTargetClasses.Classes
import scala.meta.internal.metals.MetalsEnrichments._
import scala.meta.internal.semanticdb.Scala.Descriptor
import scala.meta.internal.semanticdb.Scala.Symbols
/**
* In-memory index of main class symbols grouped by their enclosing build target
*/
final class BuildTargetClasses(
buildServer: () => Option[BuildServerConnection]
)(implicit val ec: ExecutionContext) {
private val index = TrieMap.empty[b.BuildTargetIdentifier, Classes]
val rebuildIndex: BatchedFunction[b.BuildTargetIdentifier, Unit] =
BatchedFunction.fromFuture(fetchClasses)
def classesOf(target: b.BuildTargetIdentifier): Classes = {
index.getOrElse(target, new Classes)
}
def invalidate(target: b.BuildTargetIdentifier): Unit = {
index.put(target, new Classes)
}
private def fetchClasses(
targets: Seq[b.BuildTargetIdentifier]
): Future[Unit] = {
buildServer() match {
case Some(connection) =>
val targetsList = targets.asJava
targetsList.forEach(invalidate)
val classes = targets.map(t => (t, new Classes)).toMap
val updateMainClasses = connection
.mainClasses(new b.ScalaMainClassesParams(targetsList))
.asScala
.map(cacheMainClasses(classes, _))
val updateTestClasses = connection
.testClasses(new b.ScalaTestClassesParams(targetsList))
.asScala
.map(cacheTestClasses(classes, _))
for {
_ <- updateMainClasses
_ <- updateTestClasses
} yield {
classes.foreach {
case (id, classes) => index.put(id, classes)
}
}
case None =>
Future.successful(())
}
}
private def cacheMainClasses(
classes: Map[b.BuildTargetIdentifier, Classes],
result: b.ScalaMainClassesResult
): Unit = {
for {
item <- result.getItems.asScala
target = item.getTarget
aClass <- item.getClasses.asScala
symbol <- createSymbols(aClass.getClassName, List(Descriptor.Term))
} {
classes(target).mainClasses.put(symbol, aClass)
}
}
private def cacheTestClasses(
classes: Map[b.BuildTargetIdentifier, Classes],
result: b.ScalaTestClassesResult
): Unit = {
for {
item <- result.getItems.asScala
target = item.getTarget
className <- item.getClasses.asScala
symbol <- createSymbols(className, List(Descriptor.Term, Descriptor.Type))
} {
classes(target).testClasses.put(symbol, className)
}
}
private def createSymbols(
className: String,
descriptors: List[String => Descriptor]
): List[String] = {
import scala.reflect.NameTransformer
val isEmptyPackage = !className.contains(".")
val root =
if (isEmptyPackage) Symbols.EmptyPackage
else Symbols.RootPackage
val names = className.stripSuffix("$").split("\\.")
val prefix = names.dropRight(1).foldLeft(root) { (owner, name) =>
Symbols.Global(owner, Descriptor.Package(NameTransformer.decode(name)))
}
val name = NameTransformer.decode(names.last)
descriptors.map(descriptor => Symbols.Global(prefix, descriptor(name)))
}
}
object BuildTargetClasses {
final class Classes {
val mainClasses = new TrieMap[String, b.ScalaMainClass]()
val testClasses = new TrieMap[String, String]()
def isEmpty: Boolean = mainClasses.isEmpty && testClasses.isEmpty
}
}