-
Notifications
You must be signed in to change notification settings - Fork 327
/
OutlineFilesProvider.scala
181 lines (157 loc) Β· 4.89 KB
/
OutlineFilesProvider.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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
package scala.meta.internal.metals
import java.util.Optional
import java.util.concurrent.atomic.AtomicBoolean
import java.{util => ju}
import scala.collection.concurrent.TrieMap
import scala.meta.internal.metals.MetalsEnrichments._
import scala.meta.io.AbsolutePath
import scala.meta.pc.VirtualFileParams
import scala.meta.pc.{OutlineFiles => JOutlineFiles}
import ch.epfl.scala.bsp4j.BuildTargetIdentifier
class OutlineFilesProvider(
buildTargets: BuildTargets,
buffers: Buffers,
) {
private val outlineFiles =
new TrieMap[BuildTargetIdentifier, BuildTargetOutlineFilesProvider]()
def shouldRestartPc(
id: BuildTargetIdentifier,
reason: PcRestartReason,
): Boolean = {
reason match {
case DidCompile(true) => true
case _ =>
outlineFiles.get(id) match {
case Some(provider) =>
// if it was never compiled successfully by the build server
// we don't restart pc not to lose information from outline compile
provider.wasSuccessfullyCompiledByBuildServer
case None => true
}
}
}
def onDidCompile(id: BuildTargetIdentifier, wasSuccessful: Boolean): Unit = {
outlineFiles.get(id) match {
case Some(provider) =>
if (wasSuccessful) provider.successfulCompilation()
case None =>
for {
scalaTarget <- buildTargets.scalaTarget(id)
// we don't perform outline compilation for Scala 3
if (!ScalaVersions.isScala3Version(scalaTarget.scalaVersion))
} outlineFiles.putIfAbsent(
id,
new BuildTargetOutlineFilesProvider(
buildTargets,
buffers,
id,
wasSuccessful,
),
)
}
}
def didChange(id: BuildTargetIdentifier, path: AbsolutePath): Unit = {
for {
provider <- outlineFiles.get(id)
} provider.didChange(path)
}
def getOutlineFiles(
buildTargetId: Option[BuildTargetIdentifier]
): Optional[JOutlineFiles] = {
val res: Option[JOutlineFiles] =
for {
id <- buildTargetId
provider <- outlineFiles.get(id)
outlineFiles <- provider.outlineFiles()
} yield outlineFiles
res.asJava
}
def enrichWithOutlineFiles(
buildTargetId: Option[BuildTargetIdentifier]
)(vFile: CompilerVirtualFileParams): CompilerVirtualFileParams = {
val optOutlineFiles =
for {
id <- buildTargetId
provider <- outlineFiles.get(id)
outlineFiles <- provider.outlineFiles()
} yield outlineFiles
optOutlineFiles
.map(outlineFiles => vFile.copy(outlineFiles = Optional.of(outlineFiles)))
.getOrElse(vFile)
}
def enrichWithOutlineFiles(
path: AbsolutePath
)(vFile: CompilerVirtualFileParams): CompilerVirtualFileParams =
enrichWithOutlineFiles(buildTargets.inferBuildTarget(path))(vFile)
def clear(): Unit = {
outlineFiles.clear()
}
}
class BuildTargetOutlineFilesProvider(
buildTargets: BuildTargets,
buffers: Buffers,
id: BuildTargetIdentifier,
wasCompilationSuccessful: Boolean,
) {
private val changedDocuments =
ConcurrentHashSet.empty[AbsolutePath]
private val wasAllOutlined: AtomicBoolean =
new AtomicBoolean(false)
private val wasSuccessfullyCompiled: AtomicBoolean =
new AtomicBoolean(wasCompilationSuccessful)
def wasSuccessfullyCompiledByBuildServer: Boolean =
wasSuccessfullyCompiled.get()
def successfulCompilation(): Unit = {
wasSuccessfullyCompiled.set(true)
changedDocuments.clear()
}
def didChange(path: AbsolutePath): Boolean =
changedDocuments.add(path)
def outlineFiles(): Option[OutlineFiles] = {
if (!wasSuccessfullyCompiled.get() && !wasAllOutlined.getAndSet(true)) {
// initial outline compilation that is a substitute for build server compilation
val allFiles =
buildTargets
.buildTargetSources(id)
.flatMap(_.listRecursive.toList)
.flatMap(toVirtualFileParams(_))
.toList
.asJava
Some(
OutlineFiles(
allFiles,
isFirstCompileSubstitute = true,
)
)
} else {
changedDocuments.asScala.toList.flatMap(
toVirtualFileParams
) match {
case Nil => None
case files =>
Some(
OutlineFiles(
files.asJava,
isFirstCompileSubstitute = false,
)
)
}
}
}
private def toVirtualFileParams(
path: AbsolutePath
): Option[VirtualFileParams] =
buffers.get(path).orElse(path.readTextOpt).map { text =>
CompilerVirtualFileParams(
path.toURI,
text,
)
}
}
sealed trait PcRestartReason
case object InverseDependency extends PcRestartReason
case class DidCompile(wasSuccess: Boolean) extends PcRestartReason
case class OutlineFiles(
files: ju.List[VirtualFileParams],
isFirstCompileSubstitute: Boolean = false,
) extends JOutlineFiles