Skip to content

Commit beda0b5

Browse files
authored
Merge pull request #1238 from Gedochao/interactive-global
Enable turning `--interactive` mode on permanently
2 parents 6e47361 + c965d3c commit beda0b5

File tree

5 files changed

+107
-29
lines changed

5 files changed

+107
-29
lines changed

modules/cli/src/main/scala/scala/cli/commands/util/SharedOptionsUtil.scala

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,26 @@ import dependency.AnyDependency
88
import dependency.parser.DependencyParser
99

1010
import java.io.{File, InputStream}
11-
12-
import scala.build._
11+
import scala.build.*
1312
import scala.build.blooprifle.BloopRifleConfig
1413
import scala.build.compiler.{BloopCompilerMaker, ScalaCompilerMaker, SimpleScalaCompilerMaker}
1514
import scala.build.errors.BuildException
16-
import scala.build.internal.CsLoggerUtil._
15+
import scala.build.interactive.Interactive
16+
import scala.build.interactive.Interactive.{InteractiveAsk, InteractiveNop}
17+
import scala.build.internal.CsLoggerUtil.*
1718
import scala.build.internal.{Constants, FetchExternalBinary, OsLibc, Util}
1819
import scala.build.options.{Platform, ScalacOpt, ShadowingSeq}
19-
import scala.build.{options => bo}
20+
import scala.build.options as bo
2021
import scala.cli.commands.ScalaJsOptions
21-
import scala.cli.commands.util.CommonOps._
22-
import scala.cli.commands.util.SharedCompilationServerOptionsUtil._
23-
import scala.concurrent.duration._
22+
import scala.cli.commands.util.CommonOps.*
23+
import scala.cli.commands.util.SharedCompilationServerOptionsUtil.*
24+
import scala.cli.config.{ConfigDb, Keys}
25+
import scala.concurrent.ExecutionContextExecutorService
26+
import scala.concurrent.duration.*
2427
import scala.util.Properties
2528
import scala.util.control.NonFatal
2629

27-
object SharedOptionsUtil {
30+
object SharedOptionsUtil extends CommandHelpers {
2831

2932
private def downloadInputs(cache: FileCache[Task]): String => Either[String, Array[Byte]] = {
3033
url =>
@@ -62,7 +65,7 @@ object SharedOptionsUtil {
6265
logger.message(s"WARNING: provided resource directory path doesn't exist: $path")
6366
path
6467
}
65-
.map(Inputs.ResourceDirectory(_))
68+
.map(Inputs.ResourceDirectory)
6669
val maybeInputs = Inputs(
6770
args,
6871
Os.pwd,
@@ -91,7 +94,7 @@ object SharedOptionsUtil {
9194
implicit class SharedOptionsOps(v: SharedOptions) {
9295
import v._
9396

94-
def logger = logging.logger
97+
def logger: Logger = logging.logger
9598

9699
private def scalaJsOptions(opts: ScalaJsOptions): options.ScalaJsOptions = {
97100
import opts._
@@ -165,11 +168,11 @@ object SharedOptionsUtil {
165168
scalac.scalacOption
166169
.filter(_.nonEmpty)
167170
.map(ScalacOpt(_))
168-
.map(Positioned.commandLine(_))
171+
.map(Positioned.commandLine)
169172
),
170173
compilerPlugins =
171174
SharedOptionsUtil.parseDependencies(
172-
dependencies.compilerPlugin.map(Positioned.none(_)),
175+
dependencies.compilerPlugin.map(Positioned.none),
173176
ignoreErrors
174177
),
175178
platform = platformOpt.map(o => Positioned(List(Position.CommandLine()), o))
@@ -202,7 +205,7 @@ object SharedOptionsUtil {
202205
extraRepositories = dependencies.repository.map(_.trim).filter(_.nonEmpty),
203206
extraDependencies = ShadowingSeq.from(
204207
SharedOptionsUtil.parseDependencies(
205-
dependencies.dependency.map(Positioned.none(_)),
208+
dependencies.dependency.map(Positioned.none),
206209
ignoreErrors
207210
)
208211
)
@@ -212,16 +215,59 @@ object SharedOptionsUtil {
212215
localRepository = LocalRepo.localRepo(directories.directories.localRepoDir),
213216
verbosity = Some(logging.verbosity),
214217
strictBloopJsonCheck = strictBloopJsonCheck,
215-
interactive = logging.verbosityOptions.interactive
218+
interactive = Some(() => interactive)
216219
),
217220
notForBloopOptions = bo.PostBuildOptions(
218221
scalaJsLinkerOptions = linkerOptions(js)
219222
)
220223
)
221224
}
222225

226+
def globalInteractiveWasSuggested: Option[Boolean] =
227+
configDb.getOrNone(Keys.globalInteractiveWasSuggested, logger)
228+
229+
def interactive: Interactive =
230+
(
231+
logging.verbosityOptions.interactive,
232+
configDb.getOrNone(Keys.interactive, logger),
233+
globalInteractiveWasSuggested
234+
) match {
235+
case (Some(true), _, Some(true)) => InteractiveAsk
236+
case (_, Some(true), _) => InteractiveAsk
237+
case (Some(true), _, _) =>
238+
val answers @ List(yesAnswer, _) = List("Yes", "No")
239+
InteractiveAsk.chooseOne(
240+
"""You have run the current scala-cli command with the --interactive mode turned on.
241+
|Would you like to leave it on permanently?""".stripMargin,
242+
answers
243+
) match {
244+
case Some(answer) if answer == yesAnswer =>
245+
configDb
246+
.set(Keys.interactive, true)
247+
.set(Keys.globalInteractiveWasSuggested, true)
248+
.save(v.directories.directories)
249+
logger.message(
250+
"--interactive is now set permanently. All future scala-cli commands will run with the flag set to true."
251+
)
252+
logger.message(
253+
"If you want to turn this setting off at any point, just run `scala-cli config interactive false`."
254+
)
255+
case _ =>
256+
configDb
257+
.set(Keys.globalInteractiveWasSuggested, true)
258+
.save(v.directories.directories)
259+
logger.message(
260+
"If you want to turn this setting permanently on at any point, just run `scala-cli config interactive true`."
261+
)
262+
}
263+
InteractiveAsk
264+
case _ => InteractiveNop
265+
}
266+
267+
def configDb: ConfigDb = ConfigDb.open(v).orExit(logger)
268+
223269
def downloadJvm(jvmId: String, options: bo.BuildOptions): String = {
224-
implicit val ec = options.finalCache.ec
270+
implicit val ec: ExecutionContextExecutorService = options.finalCache.ec
225271
val javaHomeManager = options.javaHomeManager
226272
.withMessage(s"Downloading JVM $jvmId")
227273
val logger = javaHomeManager.cache
@@ -306,7 +352,7 @@ object SharedOptionsUtil {
306352
!Properties.isWin
307353
)
308354

309-
def strictBloopJsonCheckOrDefault =
355+
def strictBloopJsonCheckOrDefault: Boolean =
310356
strictBloopJsonCheck.getOrElse(bo.InternalOptions.defaultStrictBloopJsonCheck)
311357
}
312358

modules/cli/src/main/scala/scala/cli/config/ConfigDb.scala

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
package scala.cli.config
22

3-
import com.github.plokhotnyuk.jsoniter_scala.core.{Key => _, _}
4-
import com.github.plokhotnyuk.jsoniter_scala.macros._
3+
import com.github.plokhotnyuk.jsoniter_scala.core.{Key as _, *}
4+
import com.github.plokhotnyuk.jsoniter_scala.macros.*
55
import coursier.parse.RawJson
66

77
import java.nio.file.attribute.PosixFilePermission
8-
9-
import scala.build.Directories
8+
import scala.build.{Directories, Logger}
109
import scala.build.errors.BuildException
10+
import scala.cli.commands.SharedOptions
11+
import scala.cli.commands.util.CommonOps.SharedDirectoriesOptionsOps
1112
import scala.collection.immutable.ListMap
1213
import scala.util.Properties
1314

@@ -42,6 +43,19 @@ final class ConfigDb private (
4243
.map(Some(_))
4344
}
4445

46+
/** Gets an entry.
47+
*
48+
* If the value cannot be decoded or the key isn't in the DB, None is returned.
49+
*
50+
* Otherwise, the value is returned wrapped in Some.
51+
*/
52+
def getOrNone[T](key: Key[T], logger: Logger): Option[T] = get[T](key) match {
53+
case Right(maybeValue) => maybeValue
54+
case Left(ex) =>
55+
logger.debug(ex)
56+
None
57+
}
58+
4559
/** Sets an entry in memory */
4660
def set[T](key: Key[T], value: T): this.type = {
4761
val b = key.write(value)
@@ -225,6 +239,16 @@ object ConfigDb {
225239
def open(directories: Directories): Either[BuildException, ConfigDb] =
226240
open(dbPath(directories))
227241

242+
/** Creates a ConfigDb from Scala CLI [[scala.cli.commands.SharedOptions]]
243+
*
244+
* @param sharedOptions:
245+
* a Scala CLI shared options instance
246+
* @return
247+
* either an error on failure, or a ConfigDb instance on success
248+
*/
249+
def open(sharedOptions: SharedOptions): Either[BuildException, ConfigDb] =
250+
open(sharedOptions.directories.directories)
251+
228252
def empty: ConfigDb =
229253
new ConfigDb(Map())
230254
}

modules/cli/src/main/scala/scala/cli/config/Keys.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,14 @@ object Keys {
1717

1818
val actions = new Key.BooleanEntry(Seq.empty, "actions")
1919

20-
def all = Seq[Key[_]](
20+
val interactive = new Key.BooleanEntry(Seq.empty, "interactive")
21+
22+
// setting indicating if the global interactive mode was suggested
23+
val globalInteractiveWasSuggested = new Key.BooleanEntry(Seq.empty, "interactive-was-suggested")
24+
25+
def all: Seq[Key[_]] = Seq[Key[_]](
26+
interactive,
27+
globalInteractiveWasSuggested,
2128
actions,
2229
userName,
2330
userEmail,
@@ -30,6 +37,6 @@ object Keys {
3037
sonatypePassword
3138
)
3239

33-
lazy val map = all.map(e => e.fullName -> e).toMap
40+
lazy val map: Map[String, Key[_]] = all.map(e => e.fullName -> e).toMap
3441

3542
}

modules/options/src/main/scala/scala/build/options/BuildOptions.scala

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -170,10 +170,10 @@ final case class BuildOptions(
170170
else None
171171
}
172172

173-
lazy val finalCache = internal.cache.getOrElse(FileCache())
173+
lazy val finalCache: FileCache[Task] = internal.cache.getOrElse(FileCache())
174174
// This might download a JVM if --jvm … is passed or no system JVM is installed
175175

176-
lazy val archiveCache = ArchiveCache().withCache(finalCache)
176+
lazy val archiveCache: ArchiveCache[Task] = ArchiveCache().withCache(finalCache)
177177

178178
private lazy val javaCommand0: Positioned[JavaHomeInfo] = javaHomeLocation().map { javaHome =>
179179
val (javaVersion, javaCmd) = OsLibc.javaHomeVersion(javaHome)
@@ -599,8 +599,7 @@ final case class BuildOptions(
599599
}
600600
}
601601

602-
val interactive: Interactive =
603-
if (internal.interactive.getOrElse(false)) InteractiveAsk else InteractiveNop
602+
lazy val interactive: Interactive = internal.interactive.getOrElse(() => InteractiveNop)()
604603
}
605604

606605
object BuildOptions {

modules/options/src/main/scala/scala/build/options/InternalOptions.scala

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import coursier.cache.FileCache
44
import coursier.util.Task
55

66
import scala.build.Positioned
7+
import scala.build.interactive.Interactive
8+
import scala.build.interactive.Interactive.InteractiveNop
79

810
final case class InternalOptions(
911
keepDiagnostics: Boolean = false,
@@ -12,7 +14,7 @@ final case class InternalOptions(
1214
verbosity: Option[Int] = None,
1315
// FIXME Should be removed, not a real option (not meant to be set from using directives)
1416
strictBloopJsonCheck: Option[Boolean] = None,
15-
interactive: Option[Boolean] = None,
17+
interactive: Option[() => Interactive] = None,
1618
javaClassNameVersionOpt: Option[String] = None,
1719
/** Whether to keep the coursier.Resolution instance in [[scala.build.Artifacts]]
1820
*
@@ -23,8 +25,8 @@ final case class InternalOptions(
2325
keepResolution: Boolean = false,
2426
extraSourceFiles: Seq[Positioned[os.Path]] = Nil
2527
) {
26-
def verbosityOrDefault = verbosity.getOrElse(0)
27-
def strictBloopJsonCheckOrDefault =
28+
def verbosityOrDefault: Int = verbosity.getOrElse(0)
29+
def strictBloopJsonCheckOrDefault: Boolean =
2830
strictBloopJsonCheck.getOrElse(InternalOptions.defaultStrictBloopJsonCheck)
2931
}
3032

0 commit comments

Comments
 (0)