Skip to content

Commit e1d3042

Browse files
authored
Merge pull request #1915 from Gedochao/specification-level-experimental
Fix handling for `experimental` features
2 parents f5872d9 + 5c44437 commit e1d3042

File tree

25 files changed

+119
-91
lines changed

25 files changed

+119
-91
lines changed

modules/build/src/main/scala/scala/build/preprocessing/DirectivesProcessor.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@ object DirectivesProcessor {
3333
scopedDirective: ScopedDirective,
3434
logger: Logger
3535
) =
36-
if (!allowRestrictedFeatures && handler.isRestricted)
36+
if (!allowRestrictedFeatures && (handler.isRestricted || handler.isExperimental))
37+
val powerDirectiveType = if handler.isExperimental then "experimental" else "restricted"
3738
val msg =
38-
"""This directive is not supported.
39-
|Please run it with the `--power` flag or turn this flag on globally by running `config power true`.""".stripMargin
39+
s"""This directive is $powerDirectiveType.
40+
|Please run it with the '--power' flag or turn this flag on globally by running 'config power true'""".stripMargin
4041
Left(DirectiveErrors(
4142
::(msg, Nil),
4243
DirectiveUtil.positions(scopedDirective.directive.values, path)

modules/cli/src/main/scala/scala/cli/commands/RestrictableCommand.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ trait RestrictableCommand[T](implicit myParser: Parser[T]) {
1010

1111
final def isRestricted: Boolean = scalaSpecificationLevel == SpecificationLevel.RESTRICTED
1212

13+
final def isExperimental: Boolean = scalaSpecificationLevel == SpecificationLevel.EXPERIMENTAL
14+
1315
/** Is that command a MUST / SHOULD / NICE TO have for the Scala runner specification? */
1416
def scalaSpecificationLevel: SpecificationLevel
1517
// To reduce imports...

modules/cli/src/main/scala/scala/cli/commands/RestrictedCommandsParser.scala

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import caseapp.core.parser.Parser
66
import caseapp.core.util.Formatter
77
import caseapp.core.{Arg, Error}
88

9+
import scala.cli.ScalaCli
910
import scala.cli.util.ArgHelpers.*
1011

1112
object RestrictedCommandsParser {
@@ -33,11 +34,13 @@ object RestrictedCommandsParser {
3334
nameFormatter: Formatter[Name]
3435
): Either[(Error, Arg, List[String]), Option[(D, Arg, List[String])]] =
3536
(parser.step(args, index, d, nameFormatter), args) match {
36-
case (Right(Some(_, arg, _)), passedOption :: _) if !arg.isSupported =>
37+
case (Right(Some(_, arg: Arg, _)), passedOption :: _) if !arg.isSupported =>
38+
val powerOptionType = if arg.isExperimental then "experimental" else "restricted"
3739
Left((
3840
Error.UnrecognizedArgument(
39-
s"""`$passedOption` option is not supported.
40-
|Please run it with the `--power` flag or turn this flag on globally by running `config power true`.""".stripMargin
41+
s"""The '$passedOption' option is $powerOptionType.
42+
|You can run it with the '--power' flag or turn the power mode on globally by running:
43+
| ${Console.BOLD}${ScalaCli.progName} config power true${Console.RESET}.""".stripMargin
4144
),
4245
arg,
4346
Nil

modules/cli/src/main/scala/scala/cli/commands/ScalaCommand.scala

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@ abstract class ScalaCommand[T <: HasLoggingOptions](implicit myParser: Parser[T]
3838

3939
def sharedOptions(t: T): Option[SharedOptions] = // hello borked unused warning
4040
None
41-
override def hasFullHelp = true
42-
override def hidden = shouldExcludeInSip
43-
protected var argvOpt = Option.empty[Array[String]]
44-
private val shouldExcludeInSip = isRestricted && !ScalaCli.allowRestrictedFeatures
41+
override def hasFullHelp = true
42+
override def hidden = shouldExcludeInSip
43+
protected var argvOpt = Option.empty[Array[String]]
44+
private val shouldExcludeInSip =
45+
(isRestricted || isExperimental) && !ScalaCli.allowRestrictedFeatures
4546
override def setArgv(argv: Array[String]): Unit = {
4647
argvOpt = Some(argv)
4748
}
@@ -275,7 +276,9 @@ abstract class ScalaCommand[T <: HasLoggingOptions](implicit myParser: Parser[T]
275276

276277
override val messages: Help[T] =
277278
if (shouldExcludeInSip)
278-
Help[T](helpMessage = Some(HelpMessage(HelpMessages.restrictedCommandUsedInSip)))
279+
Help[T](helpMessage =
280+
Some(HelpMessage(HelpMessages.powerCommandUsedInSip(scalaSpecificationLevel)))
281+
)
279282
else help
280283

281284
/** @param options
@@ -307,7 +310,7 @@ abstract class ScalaCommand[T <: HasLoggingOptions](implicit myParser: Parser[T]
307310
*/
308311
final override def run(options: T, remainingArgs: RemainingArgs): Unit = {
309312
if (shouldExcludeInSip)
310-
System.err.println(HelpMessages.restrictedCommandUsedInSip)
313+
System.err.println(HelpMessages.powerCommandUsedInSip(scalaSpecificationLevel))
311314
sys.exit(1)
312315
CurrentParams.verbosity = options.logging.verbosity
313316
maybePrintWarnings(options)

modules/cli/src/main/scala/scala/cli/commands/export0/Export.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import scala.cli.util.ArgHelpers.*
2525
import scala.util.Using
2626

2727
object Export extends ScalaCommand[ExportOptions] {
28-
override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.RESTRICTED
28+
override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.EXPERIMENTAL
2929

3030
override def helpFormat: HelpFormat =
3131
super.helpFormat.withPrimaryGroup(HelpGroup.BuildToolExport)

modules/cli/src/main/scala/scala/cli/commands/github/SecretCreate.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import scala.cli.util.ArgHelpers.*
2121

2222
object SecretCreate extends ScalaCommand[SecretCreateOptions] {
2323

24-
override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.RESTRICTED
24+
override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.EXPERIMENTAL
2525
override def helpFormat: HelpFormat = super.helpFormat.withPrimaryGroup(HelpGroup.Secret)
2626
override def names = List(
2727
List("github", "secret", "create"),

modules/cli/src/main/scala/scala/cli/commands/github/SecretList.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import scala.cli.util.ArgHelpers.*
1717

1818
object SecretList extends ScalaCommand[SecretListOptions] {
1919

20-
override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.RESTRICTED
20+
override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.EXPERIMENTAL
2121

2222
override def helpFormat: HelpFormat = super.helpFormat.withPrimaryGroup(HelpGroup.Secret)
2323
override def names = List(

modules/cli/src/main/scala/scala/cli/commands/pgp/PgpCommand.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ abstract class PgpCommand[T](implicit myParser: Parser[T], help: Help[T])
1212
extends Command()(myParser, help)
1313
with CommandHelpers with RestrictableCommand[T] {
1414

15-
override def scalaSpecificationLevel = SpecificationLevel.RESTRICTED
15+
override def scalaSpecificationLevel = SpecificationLevel.EXPERIMENTAL
1616

1717
override def hidden = true
1818
}

modules/cli/src/main/scala/scala/cli/commands/pgp/PgpPull.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import scala.cli.commands.util.ScalaCliSttpBackend
1010
object PgpPull extends ScalaCommand[PgpPullOptions] {
1111

1212
override def hidden = true
13-
override def scalaSpecificationLevel = SpecificationLevel.RESTRICTED
13+
override def scalaSpecificationLevel = SpecificationLevel.EXPERIMENTAL
1414
override def names = List(
1515
List("pgp", "pull")
1616
)

modules/cli/src/main/scala/scala/cli/commands/pgp/PgpPush.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import scala.cli.internal.PgpProxyMakerSubst
1212
object PgpPush extends ScalaCommand[PgpPushOptions] {
1313

1414
override def hidden = true
15-
override def scalaSpecificationLevel = SpecificationLevel.RESTRICTED
15+
override def scalaSpecificationLevel = SpecificationLevel.EXPERIMENTAL
1616
override def names = List(
1717
List("pgp", "push")
1818
)

modules/cli/src/main/scala/scala/cli/commands/publish/Publish.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ import scala.util.control.NonFatal
6969

7070
object Publish extends ScalaCommand[PublishOptions] with BuildCommandHelpers {
7171

72-
override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.RESTRICTED
72+
override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.EXPERIMENTAL
7373

7474
import scala.cli.commands.shared.HelpGroup.*
7575
val primaryHelpGroups: Seq[HelpGroup] = Seq(Publishing, Signing, PGP)

modules/cli/src/main/scala/scala/cli/commands/publish/PublishLocal.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import scala.cli.util.ArgHelpers.*
1414
object PublishLocal extends ScalaCommand[PublishLocalOptions] {
1515

1616
override def group: String = HelpCommandGroup.Main.toString
17-
override def scalaSpecificationLevel = SpecificationLevel.RESTRICTED
17+
override def scalaSpecificationLevel = SpecificationLevel.EXPERIMENTAL
1818
override def helpFormat: HelpFormat =
1919
super.helpFormat
2020
.withHiddenGroups(Publish.hiddenHelpGroups)

modules/cli/src/main/scala/scala/cli/commands/publish/PublishSetup.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import scala.cli.util.ArgHelpers.*
2424
object PublishSetup extends ScalaCommand[PublishSetupOptions] {
2525

2626
override def group: String = HelpCommandGroup.Main.toString
27-
override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.RESTRICTED
27+
override def scalaSpecificationLevel: SpecificationLevel = SpecificationLevel.EXPERIMENTAL
2828

2929
override def helpFormat: HelpFormat =
3030
super.helpFormat

modules/cli/src/main/scala/scala/cli/commands/shared/HelpMessages.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package scala.cli.commands.shared
22

33
import scala.cli.ScalaCli
4+
import scala.cli.commands.SpecificationLevel
45

56
object HelpMessages {
67
lazy val PowerString: String = if ScalaCli.allowRestrictedFeatures then "" else "--power "
@@ -47,8 +48,11 @@ object HelpMessages {
4748
s"""Specific $cmdName configurations can be specified with both command line options and using directives defined in sources.
4849
|Command line options always take priority over using directives when a clash occurs, allowing to override configurations defined in sources.
4950
|Using directives can be defined in all supported input source file types.""".stripMargin
50-
lazy val restrictedCommandUsedInSip: String =
51-
s"""This command is restricted and requires setting the `--power` option to be used.
51+
def powerCommandUsedInSip(specificationLevel: SpecificationLevel): String = {
52+
val powerCommandType =
53+
if specificationLevel == SpecificationLevel.EXPERIMENTAL then "experimental" else "restricted"
54+
s"""This command is $powerCommandType and requires setting the '--power' launcher option to be used.
5255
|You can pass it explicitly or set it globally by running:
5356
| ${Console.BOLD}${ScalaCli.progName} config power true${Console.RESET}""".stripMargin
57+
}
5458
}

modules/cli/src/main/scala/scala/cli/util/ArgHelpers.scala

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,16 @@ import scala.cli.commands.{SpecificationLevel, tags}
99

1010
object ArgHelpers {
1111
extension (arg: Arg) {
12-
def isExperimentalOrRestricted: Boolean =
13-
arg.tags.exists(_.name == tags.restricted) || arg.tags.exists(_.name == tags.experimental)
12+
private def hasTag(tag: String): Boolean = arg.tags.exists(_.name == tag)
13+
def isExperimental: Boolean = arg.hasTag(tags.experimental)
14+
def isRestricted: Boolean = arg.hasTag(tags.restricted)
15+
16+
def isExperimentalOrRestricted: Boolean = arg.isRestricted || arg.isExperimental
1417

1518
def isSupported: Boolean = allowRestrictedFeatures || !arg.isExperimentalOrRestricted
16-
def isImportant: Boolean = arg.tags.exists(_.name == tags.inShortHelp)
19+
def isImportant: Boolean = arg.hasTag(tags.inShortHelp)
1720

18-
def isMust: Boolean = arg.tags.exists(_.name == tags.must)
21+
def isMust: Boolean = arg.hasTag(tags.must)
1922

2023
def level: SpecificationLevel = arg.tags
2124
.flatMap(t => tags.levelFor(t.name))

modules/directives/src/main/scala/scala/build/preprocessing/directives/DirectiveHandler.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ trait DirectiveHandler[+T] { self =>
3434
def scalaSpecificationLevel: SpecificationLevel
3535
protected def SpecificationLevel = scala.cli.commands.SpecificationLevel
3636

37-
final def isRestricted: Boolean = scalaSpecificationLevel == SpecificationLevel.RESTRICTED
37+
final def isRestricted: Boolean = scalaSpecificationLevel == SpecificationLevel.RESTRICTED
38+
final def isExperimental: Boolean = scalaSpecificationLevel == SpecificationLevel.EXPERIMENTAL
3839

3940
def keys: Seq[String]
4041

modules/directives/src/main/scala/scala/build/preprocessing/directives/Publish.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import scala.cli.commands.SpecificationLevel
2222
|""".stripMargin
2323
)
2424
@DirectiveDescription("Set parameters for publishing")
25-
@DirectiveLevel(SpecificationLevel.RESTRICTED)
25+
@DirectiveLevel(SpecificationLevel.EXPERIMENTAL)
2626
// format: off
2727
final case class Publish(
2828
organization: Option[Positioned[String]] = None,

modules/directives/src/main/scala/scala/build/preprocessing/directives/PublishContextual.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ object PublishContextual {
9999
|""".stripMargin
100100
)
101101
@DirectiveDescription("Set contextual parameters for publishing")
102-
@DirectiveLevel(SpecificationLevel.RESTRICTED)
102+
@DirectiveLevel(SpecificationLevel.EXPERIMENTAL)
103103
// format: off
104104
final case class Local(
105105
computeVersion: Option[Positioned[String]] = None,

modules/directives/src/main/scala/scala/build/preprocessing/directives/RequirePlatform.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import scala.cli.commands.SpecificationLevel
1717
"//> using target.platform _platform_",
1818
"`//> using target.platform `_platform_"
1919
)
20-
@DirectiveLevel(SpecificationLevel.RESTRICTED)
20+
@DirectiveLevel(SpecificationLevel.EXPERIMENTAL)
2121
// format: off
2222
final case class RequirePlatform(
2323
@DirectiveName("platform")

modules/directives/src/main/scala/scala/build/preprocessing/directives/RequireScalaVersion.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import scala.cli.commands.SpecificationLevel
1313
"//> using target.scala _version_",
1414
"`//> using target.scala `_version_"
1515
)
16-
@DirectiveLevel(SpecificationLevel.RESTRICTED)
16+
@DirectiveLevel(SpecificationLevel.EXPERIMENTAL)
1717
final case class RequireScalaVersion(
1818
scala: Option[DirectiveValueParser.MaybeNumericalString] = None
1919
) extends HasBuildRequirements {

modules/directives/src/main/scala/scala/build/preprocessing/directives/RequireScalaVersionBounds.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import scala.cli.commands.SpecificationLevel
1414
"//> using target.scala.>= _version_",
1515
"`//> using target.scala.>= `_version_"
1616
)
17-
@DirectiveLevel(SpecificationLevel.RESTRICTED)
17+
@DirectiveLevel(SpecificationLevel.EXPERIMENTAL)
1818
final case class RequireScalaVersionBounds(
1919
`==`: Option[DirectiveValueParser.MaybeNumericalString] = None,
2020
`>=`: Option[DirectiveValueParser.MaybeNumericalString] = None,

modules/directives/src/main/scala/scala/build/preprocessing/directives/RequireScope.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import scala.cli.commands.SpecificationLevel
1616
"//> using target.scope _scope_",
1717
"`//> using target.scope `_scope_"
1818
)
19-
@DirectiveLevel(SpecificationLevel.RESTRICTED)
19+
@DirectiveLevel(SpecificationLevel.EXPERIMENTAL)
2020
final case class RequireScope(
2121
scope: Option[Positioned[String]] = None
2222
) extends HasBuildRequirements {

modules/generate-reference-doc/src/main/scala/scala/cli/doc/GenerateReferenceDoc.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,9 @@ object GenerateReferenceDoc extends CaseApp[InternalDocOptions] {
489489
)
490490
val commands = scalaCli.commands
491491
val restrictedCommands =
492-
commands.iterator.collect { case s: ScalaCommand[_] if !s.isRestricted => s }.toSeq
492+
commands.iterator.collect {
493+
case s: ScalaCommand[_] if !s.isRestricted && !s.isExperimental => s
494+
}.toSeq
493495
val allArgs = commands.flatMap(actualHelp(_).args)
494496
val nameFormatter = scalaCli.actualDefaultCommand.nameFormatter
495497

modules/integration/src/test/scala/scala/cli/integration/RunScalaPyTestDefinitions.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ trait RunScalaPyTestDefinitions { _: RunTestDefinitions =>
108108
inputs.fromRoot { root =>
109109

110110
// Script dir shouldn't be added to PYTHONPATH if PYTHONSAFEPATH is non-empty
111-
val errorRes = os.proc(TestUtil.cli, "run", extraOptions, nativeOpt, "src")
111+
val errorRes = os.proc(TestUtil.cli, "--power", "run", extraOptions, nativeOpt, "src")
112112
.call(
113113
cwd = root,
114114
env = Map("PYTHONSAFEPATH" -> "foo"),
@@ -119,7 +119,7 @@ trait RunScalaPyTestDefinitions { _: RunTestDefinitions =>
119119
val errorOutput = errorRes.out.text()
120120
expect(errorOutput.contains("No module named 'helpers'"))
121121

122-
val res = os.proc(TestUtil.cli, "run", extraOptions, nativeOpt, "src")
122+
val res = os.proc(TestUtil.cli, "--power", "run", extraOptions, nativeOpt, "src")
123123
.call(cwd = root)
124124
val output = res.out.trim()
125125
if (native)

0 commit comments

Comments
 (0)