@@ -49,6 +49,15 @@ final class BloopServers(
49
49
50
50
import BloopServers ._
51
51
52
+ private val bloopJsonPath : Option [AbsolutePath ] =
53
+ getBloopFilePath(fileName = " bloop.json" )
54
+ private val bloopLockFile : Option [AbsolutePath ] =
55
+ getBloopFilePath(fileName = " created_by_metals.lock" )
56
+
57
+ private def bloopLastModifiedTime : Long = bloopJsonPath
58
+ .flatMap(path => Try (path.toNIO.toFile().lastModified()).toOption)
59
+ .getOrElse(0L )
60
+
52
61
def shutdownServer (): Boolean = {
53
62
val dummyIn = new ByteArrayInputStream (new Array (0 ))
54
63
val cli = new BloopgunCli (
@@ -78,7 +87,8 @@ final class BloopServers(
78
87
workspace,
79
88
client,
80
89
languageClient,
81
- () => connectToLauncher(bloopVersion, config.bloopPort),
90
+ () =>
91
+ connectToLauncher(bloopVersion, config.bloopPort, userConfiguration),
82
92
tables.dismissedNotifications.ReconnectBsp ,
83
93
config,
84
94
name
@@ -129,56 +139,41 @@ final class BloopServers(
129
139
}
130
140
131
141
private def writeJVMPropertiesToBloopGlobalJsonFile (
132
- bloopGlobalJsonFilePath : AbsolutePath ,
133
- bloopCreatedByMetalsFilePath : AbsolutePath ,
134
- maybeBloopJvmProperties : Option [List [String ]],
142
+ maybeBloopJvmProperties : List [String ],
135
143
maybeJavaHome : Option [String ]
136
144
): Try [Unit ] = Try {
137
-
138
- val javaOptionsString = maybeBloopJvmProperties
139
- .map { bloopJvmProperties =>
140
- s " \" javaOptions \" : [ ${bloopJvmProperties.map(property => s " \" $property\" " ).mkString(" , " )}] "
141
- }
142
- .getOrElse(" " )
143
-
144
- val jvmPropertiesString = maybeJavaHome
145
- .map { javaHome =>
146
- if (javaOptionsString.isEmpty)
147
- s " { \" javaHome \" : \" $javaHome\" } "
148
- else
149
- s """ |{
150
- | $javaOptionsString,
151
- | \"javaHome\": \" $javaHome\"
152
- |} """ .stripMargin
153
- }
154
- .getOrElse(s " { $javaOptionsString} " )
155
- bloopGlobalJsonFilePath.writeText(jvmPropertiesString)
156
- bloopCreatedByMetalsFilePath.writeText(
157
- bloopGlobalJsonFilePath.toNIO.toFile.lastModified().toString
158
- )
145
+ if (maybeJavaHome.isDefined || maybeBloopJvmProperties.nonEmpty) {
146
+ val javaOptionsField =
147
+ if (maybeBloopJvmProperties.nonEmpty)
148
+ Some (
149
+ " javaOptions" -> ujson.Arr (
150
+ maybeBloopJvmProperties.map(opt => ujson.Str (opt.trim())): _*
151
+ )
152
+ )
153
+ else None
154
+ val fields : List [(String , ujson.Value )] =
155
+ List (
156
+ maybeJavaHome.map(v => " javaHome" -> ujson.Str (v.trim())),
157
+ javaOptionsField
158
+ ).flatten
159
+ val obj = ujson.Obj .from(fields)
160
+ val jvmPropertiesString = ujson.write(obj)
161
+
162
+ bloopJsonPath.foreach(_.writeText(jvmPropertiesString))
163
+ bloopLockFile.foreach(_.writeText(bloopLastModifiedTime.toString()))
164
+ }
159
165
}
160
166
161
- private def getBloopGlobalJsonLastModifiedByMetalsTime (
162
- bloopCreatedByMetalsFilePath : AbsolutePath
163
- ): Long = Try {
164
- bloopCreatedByMetalsFilePath.readText.toLong
165
- }.getOrElse(0 )
166
-
167
167
private def processUserPreferenceForBloopJvmProperties (
168
168
messageActionItem : MessageActionItem ,
169
- bloopGlobalJsonFilePath : AbsolutePath ,
170
- bloopCreatedByMetalsFilePath : AbsolutePath ,
171
- maybeBloopJvmProperties : Option [List [String ]],
169
+ maybeBloopJvmProperties : List [String ],
172
170
maybeJavaHome : Option [String ],
173
171
reconnect : () => Future [BuildChange ]
174
172
): Future [Unit ] = {
175
- messageActionItem match {
176
-
177
- case item
173
+ (messageActionItem, bloopJsonPath) match {
174
+ case (item, _)
178
175
if item == Messages .BloopGlobalJsonFilePremodified .applyAndRestart =>
179
176
writeJVMPropertiesToBloopGlobalJsonFile(
180
- bloopGlobalJsonFilePath,
181
- bloopCreatedByMetalsFilePath,
182
177
maybeBloopJvmProperties,
183
178
maybeJavaHome
184
179
) match {
@@ -188,70 +183,65 @@ final class BloopServers(
188
183
reconnect().ignoreValue
189
184
}
190
185
191
- case item
186
+ case ( item, Some (bloopPath))
192
187
if item == Messages .BloopGlobalJsonFilePremodified .openGlobalJsonFile =>
193
188
val position = new Position (0 , 0 )
194
189
val range = new org.eclipse.lsp4j.Range (position, position)
195
190
val command = ClientCommands .GotoLocation .toExecuteCommandParams(
196
191
ClientCommands .WindowLocation (
197
- bloopGlobalJsonFilePath .toURI.toString,
192
+ bloopPath .toURI.toString,
198
193
range
199
194
)
200
195
)
201
196
Future .successful(languageClient.metalsExecuteClientCommand(command))
202
197
203
- case item
204
- if item == Messages .BloopGlobalJsonFilePremodified .useGlobalFile =>
205
- Future .unit
198
+ case _ => Future .unit
199
+
206
200
}
207
201
}
208
202
209
203
private def updateBloopGlobalJsonFileThenRestart (
210
- bloopGlobalJsonFilePath : AbsolutePath ,
211
- bloopCreatedByMetalsFilePath : AbsolutePath ,
212
- maybeBloopJvmProperties : Option [List [String ]],
204
+ maybeBloopJvmProperties : List [String ],
213
205
maybeJavaHome : Option [String ],
214
- bloopJsonUpdateCause : BloopJsonUpdateCause ,
215
- reconnect : () => Future [ BuildChange ]
206
+ reconnect : () => Future [ BuildChange ] ,
207
+ bloopJsonUpdateCause : BloopJsonUpdateCause
216
208
): Future [Unit ] = {
217
- writeJVMPropertiesToBloopGlobalJsonFile(
218
- bloopGlobalJsonFilePath,
219
- bloopCreatedByMetalsFilePath,
220
- maybeBloopJvmProperties,
221
- maybeJavaHome
222
- ) match {
223
- case Failure (exception) => Future .failed(exception)
224
- case Success (_) =>
225
- languageClient
226
- .showMessageRequest(
227
- Messages .BloopJvmPropertiesChange .params(bloopJsonUpdateCause)
228
- )
229
- .asScala
230
- .flatMap {
231
- case messageActionItem
232
- if messageActionItem == Messages .BloopJvmPropertiesChange .reconnect =>
209
+ languageClient
210
+ .showMessageRequest(
211
+ Messages .BloopJvmPropertiesChange .params(bloopJsonUpdateCause)
212
+ )
213
+ .asScala
214
+ .flatMap {
215
+ case messageActionItem
216
+ if messageActionItem == Messages .BloopJvmPropertiesChange .reconnect =>
217
+ writeJVMPropertiesToBloopGlobalJsonFile(
218
+ maybeBloopJvmProperties,
219
+ maybeJavaHome
220
+ ) match {
221
+ case Failure (exception) => Future .failed(exception)
222
+ case Success (_) =>
233
223
shutdownServer()
234
224
reconnect().ignoreValue
235
- case _ =>
236
- Future .unit
237
225
}
238
- }
226
+ case _ =>
227
+ Future .unit
228
+ }
239
229
240
230
}
241
231
242
232
private def getBloopFilePath (fileName : String ): Option [AbsolutePath ] = {
243
- Try {
233
+ sys.props.get( " user.home " ).map { home =>
244
234
AbsolutePath (
245
235
Paths
246
- .get(System .getProperty( " user. home" ) )
236
+ .get(home)
247
237
.resolve(s " .bloop/ $fileName" )
248
238
)
249
- }.toOption
239
+ }
250
240
}
251
241
252
242
private def maybeLoadBloopGlobalJsonFile (
253
243
bloopGlobalJsonFilePath : AbsolutePath
254
- ): (Option [String ], Option [ List [String ] ]) = {
244
+ ): (Option [String ], List [String ]) = {
255
245
256
246
val maybeLinkedHashMap =
257
247
bloopGlobalJsonFilePath.readTextOpt.map(ujson.read(_)).flatMap(_.objOpt)
@@ -267,16 +257,9 @@ final class BloopServers(
267
257
javaOptionsValue <- linkedHashMap.get(" javaOptions" )
268
258
javaOptionsValueArray <- javaOptionsValue.arrOpt
269
259
} yield javaOptionsValueArray.flatMap(_.strOpt).toList
270
- (maybeJavaHome, maybeJavaOptions)
260
+ (maybeJavaHome, maybeJavaOptions.getOrElse( Nil ) )
271
261
}
272
262
273
- private def getBloopGlobalJsonLastModifiedTime (
274
- bloopGlobalJsonFilePath : AbsolutePath
275
- ): Long =
276
- Try {
277
- bloopGlobalJsonFilePath.toNIO.toFile.lastModified()
278
- }.getOrElse(0 )
279
-
280
263
/**
281
264
* First we check if the user requested to update the Bloop JVM
282
265
* properties through the extension.
@@ -304,31 +287,26 @@ final class BloopServers(
304
287
maybeRequestedMetalsJavaHome : Option [String ],
305
288
reconnect : () => Future [BuildChange ]
306
289
): Future [Unit ] = {
307
-
308
290
val result =
309
- for { // if bloopGlobalJsonFilePath is defined
310
- bloopGlobalJsonFilePath <- getBloopFilePath(fileName = " bloop.json" )
311
- bloopCreatedByMetalsFilePath <- getBloopFilePath(fileName =
312
- " created_by_metals.lock"
313
- )
291
+ for {
292
+ bloopPath <- bloopJsonPath
314
293
(maybeBloopGlobalJsonJavaHome, maybeBloopGlobalJsonJvmProperties) =
315
- maybeLoadBloopGlobalJsonFile(bloopGlobalJsonFilePath)
316
-
294
+ maybeLoadBloopGlobalJsonFile(bloopPath)
317
295
bloopJsonUpdateCause <-
318
296
if (
319
- maybeRequestedBloopJvmProperties != maybeBloopGlobalJsonJvmProperties
320
- && maybeRequestedBloopJvmProperties.nonEmpty
297
+ maybeRequestedBloopJvmProperties
298
+ .exists(requested =>
299
+ requested != maybeBloopGlobalJsonJvmProperties
300
+ )
321
301
) Some (BloopJsonUpdateCause .JVM_OPTS )
322
302
else if (maybeRequestedMetalsJavaHome != maybeBloopGlobalJsonJavaHome)
323
303
Some (BloopJsonUpdateCause .JAVA_HOME )
324
304
else None
325
- maybeBloopJvmProperties = maybeRequestedBloopJvmProperties.orElse (
305
+ maybeBloopJvmProperties = maybeRequestedBloopJvmProperties.getOrElse (
326
306
maybeBloopGlobalJsonJvmProperties
327
307
)
328
308
} yield updateBloopJvmProperties(
329
309
maybeBloopJvmProperties,
330
- bloopGlobalJsonFilePath,
331
- bloopCreatedByMetalsFilePath,
332
310
maybeRequestedMetalsJavaHome,
333
311
reconnect,
334
312
bloopJsonUpdateCause
@@ -340,20 +318,16 @@ final class BloopServers(
340
318
}
341
319
342
320
private def updateBloopJvmProperties (
343
- maybeBloopJvmProperties : Option [List [String ]],
344
- bloopGlobalJsonFilePath : AbsolutePath ,
345
- bloopCreatedByMetalsFilePath : AbsolutePath ,
321
+ maybeBloopJvmProperties : List [String ],
346
322
maybeJavaHome : Option [String ],
347
323
reconnect : () => Future [BuildChange ],
348
324
bloopJsonUpdateCause : BloopJsonUpdateCause
349
- ): Future [Unit ] = { // the properties are updated
325
+ ): Future [Unit ] = {
326
+ val lockFileTime = bloopLockFile
327
+ .flatMap(file => Try (file.readText.toLong).toOption)
328
+ .getOrElse(0L )
350
329
if (
351
- bloopGlobalJsonFilePath.exists &&
352
- getBloopGlobalJsonLastModifiedTime(
353
- bloopGlobalJsonFilePath
354
- ) > getBloopGlobalJsonLastModifiedByMetalsTime(
355
- bloopCreatedByMetalsFilePath
356
- )
330
+ bloopJsonPath.exists(_.exists) && bloopLastModifiedTime > lockFileTime
357
331
) {
358
332
// the global json file was previously modified by the user through other means;
359
333
// therefore overwriting it requires user input
@@ -365,8 +339,6 @@ final class BloopServers(
365
339
.flatMap {
366
340
processUserPreferenceForBloopJvmProperties(
367
341
_,
368
- bloopGlobalJsonFilePath,
369
- bloopCreatedByMetalsFilePath,
370
342
maybeBloopJvmProperties,
371
343
maybeJavaHome,
372
344
reconnect
@@ -384,12 +356,10 @@ final class BloopServers(
384
356
// hence it can get created or overwritten by Metals with no worries
385
357
// about overriding the user preferred settings
386
358
updateBloopGlobalJsonFileThenRestart(
387
- bloopGlobalJsonFilePath,
388
- bloopCreatedByMetalsFilePath,
389
359
maybeBloopJvmProperties,
390
360
maybeJavaHome,
391
- bloopJsonUpdateCause ,
392
- reconnect
361
+ reconnect ,
362
+ bloopJsonUpdateCause
393
363
) andThen {
394
364
case Failure (exception) =>
395
365
languageClient.showMessage(
@@ -403,8 +373,21 @@ final class BloopServers(
403
373
404
374
private def connectToLauncher (
405
375
bloopVersion : String ,
406
- bloopPort : Option [Int ]
376
+ bloopPort : Option [Int ],
377
+ userConfiguration : UserConfiguration
407
378
): Future [SocketConnection ] = {
379
+ // we should set up Java before running Bloop in order to not restart it
380
+ bloopJsonPath match {
381
+ case Some (bloopPath) if ! bloopPath.exists =>
382
+ // we want to use the same java version as Metals, so it's ok to use java.home
383
+ val metalsJavaHome =
384
+ userConfiguration.javaHome.orElse(sys.props.get(" java.home" ))
385
+ writeJVMPropertiesToBloopGlobalJsonFile(
386
+ userConfiguration.bloopJvmProperties.getOrElse(Nil ),
387
+ metalsJavaHome
388
+ )
389
+ case _ =>
390
+ }
408
391
val launcherInOutPipe = Pipe .open()
409
392
val launcherIn = new QuietInputStream (
410
393
Channels .newInputStream(launcherInOutPipe.source()),
0 commit comments