Skip to content

Commit 864e1a5

Browse files
authored
Merge pull request #1910 from Gedochao/config-keys-reference
Add a reference for available config keys in help & docs
2 parents 6df89de + e52f002 commit 864e1a5

File tree

9 files changed

+287
-67
lines changed

9 files changed

+287
-67
lines changed

modules/cli/src/main/scala/scala/cli/commands/config/ConfigOptions.scala

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

33
import caseapp.*
44

5+
import scala.build.internal.util.ConsoleUtils.ScalaCliConsole
56
import scala.cli.ScalaCli.{fullRunnerName, progName}
67
import scala.cli.commands.pgp.PgpScalaSigningOptions
78
import scala.cli.commands.shared.{
@@ -13,6 +14,7 @@ import scala.cli.commands.shared.{
1314
SharedJvmOptions
1415
}
1516
import scala.cli.commands.tags
17+
import scala.cli.config.{Key, Keys}
1618

1719
// format: off
1820
@HelpMessage(ConfigOptions.helpMessage, "", ConfigOptions.detailedHelpMessage)
@@ -86,19 +88,38 @@ object ConfigOptions {
8688
implicit lazy val help: Help[ConfigOptions] = Help.derive
8789
private val helpHeader: String = s"Configure global settings for $fullRunnerName."
8890
private val cmdName = "config"
89-
private val websiteSuffix = s"misc/$cmdName"
9091
val helpMessage: String =
9192
s"""$helpHeader
93+
|
94+
|Available keys:
95+
| ${configKeyMessages(includeHidden = false).mkString(s"${System.lineSeparator} ")}
9296
|
9397
|${HelpMessages.commandFullHelpReference(cmdName)}
94-
|${HelpMessages.commandDocWebsiteReference(websiteSuffix)}""".stripMargin
98+
|${HelpMessages.commandDocWebsiteReference(cmdName)}""".stripMargin
99+
private def configKeyMessages(includeHidden: Boolean): Seq[String] = {
100+
val allKeys: Seq[Key[_]] = Keys.map.values.toSeq
101+
val keys: Seq[Key[_]] = if includeHidden then allKeys else allKeys.filterNot(_.hidden)
102+
val maxFullNameLength = keys.map(_.fullName.length).max
103+
keys.sortBy(_.fullName)
104+
.map { key =>
105+
val currentKeyFullNameLength = maxFullNameLength - key.fullName.length
106+
val extraSpaces =
107+
if currentKeyFullNameLength > 0 then " " * currentKeyFullNameLength else ""
108+
val hiddenString =
109+
if key.hidden then s"${ScalaCliConsole.GRAY}(hidden)${Console.RESET} " else ""
110+
s"${Console.YELLOW}${key.fullName}${Console.RESET}$extraSpaces $hiddenString${key.description}"
111+
}
112+
}
95113
val detailedHelpMessage: String =
96114
s"""$helpHeader
97115
|
98116
|Syntax:
99117
| ${Console.BOLD}$progName $cmdName key value${Console.RESET}
100118
|For example, to globally set the interactive mode:
101119
| ${Console.BOLD}$progName $cmdName interactive true${Console.RESET}
120+
|
121+
|Available keys:
122+
| ${configKeyMessages(includeHidden = true).mkString(s"${System.lineSeparator} ")}
102123
|
103-
|${HelpMessages.commandDocWebsiteReference(websiteSuffix)}""".stripMargin
124+
|${HelpMessages.commandDocWebsiteReference(cmdName)}""".stripMargin
104125
}

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

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,13 @@ abstract class Key[T] {
3636
def fromString(values: Seq[String]): Either[Key.MalformedValue, T]
3737

3838
/** The fully qualified name of this key */
39-
final def fullName = (prefix :+ name).mkString(".")
39+
final def fullName: String = (prefix :+ name).mkString(".")
40+
41+
/** A short description of a particular key's purpose and syntax for its values. */
42+
def description: String
43+
44+
/** A flag indicating whether the key should by default be hidden in help outputs or not. */
45+
def hidden: Boolean = false
4046

4147
/** Whether this key corresponds to a password (see [[Key.PasswordEntry]]) */
4248
def isPasswordOption: Boolean = false
@@ -76,7 +82,9 @@ object Key {
7682

7783
final class StringEntry(
7884
val prefix: Seq[String],
79-
val name: String
85+
val name: String,
86+
val description: String = "",
87+
override val hidden: Boolean = false
8088
) extends Key[String] {
8189
def parse(json: Array[Byte]): Either[EntryError, String] =
8290
try Right(readFromArray(json)(stringCodec))
@@ -97,7 +105,9 @@ object Key {
97105

98106
final class BooleanEntry(
99107
val prefix: Seq[String],
100-
val name: String
108+
val name: String,
109+
val description: String = "",
110+
override val hidden: Boolean = false
101111
) extends Key[Boolean] {
102112
def parse(json: Array[Byte]): Either[EntryError, Boolean] =
103113
try Right(readFromArray(json)(booleanCodec))
@@ -118,7 +128,9 @@ object Key {
118128

119129
final class PasswordEntry(
120130
val prefix: Seq[String],
121-
val name: String
131+
val name: String,
132+
val description: String = "",
133+
override val hidden: Boolean = false
122134
) extends Key[PasswordOption] {
123135
def parse(json: Array[Byte]): Either[EntryError, PasswordOption] =
124136
try {
@@ -150,7 +162,9 @@ object Key {
150162

151163
final class StringListEntry(
152164
val prefix: Seq[String],
153-
val name: String
165+
val name: String,
166+
val description: String = "",
167+
override val hidden: Boolean = false
154168
) extends Key[List[String]] {
155169
def parse(json: Array[Byte]): Either[EntryError, List[String]] =
156170
try Right(readFromArray(json)(stringListCodec))

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

Lines changed: 112 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,37 +7,125 @@ import scala.collection.mutable.ListBuffer
77

88
object Keys {
99

10-
val userName = new Key.StringEntry(Seq("publish", "user"), "name")
11-
val userEmail = new Key.StringEntry(Seq("publish", "user"), "email")
12-
val userUrl = new Key.StringEntry(Seq("publish", "user"), "url")
10+
val userName = new Key.StringEntry(
11+
prefix = Seq("publish", "user"),
12+
name = "name",
13+
description = "The 'name' user detail, used for publishing.",
14+
hidden = true
15+
)
16+
val userEmail = new Key.StringEntry(
17+
prefix = Seq("publish", "user"),
18+
name = "email",
19+
description = "The 'email' user detail, used for publishing.",
20+
hidden = true
21+
)
22+
val userUrl = new Key.StringEntry(
23+
prefix = Seq("publish", "user"),
24+
name = "url",
25+
description = "The 'url' user detail, used for publishing.",
26+
hidden = true
27+
)
1328

14-
val ghToken = new Key.PasswordEntry(Seq("github"), "token")
29+
val ghToken = new Key.PasswordEntry(
30+
prefix = Seq("github"),
31+
name = "token",
32+
description = "GitHub token.",
33+
hidden = true
34+
)
1535

16-
val pgpSecretKey = new Key.PasswordEntry(Seq("pgp"), "secret-key")
17-
val pgpSecretKeyPassword = new Key.PasswordEntry(Seq("pgp"), "secret-key-password")
18-
val pgpPublicKey = new Key.PasswordEntry(Seq("pgp"), "public-key")
36+
val pgpSecretKey = new Key.PasswordEntry(
37+
prefix = Seq("pgp"),
38+
name = "secret-key",
39+
description = "The PGP secret key, used for signing.",
40+
hidden = true
41+
)
42+
val pgpSecretKeyPassword = new Key.PasswordEntry(
43+
prefix = Seq("pgp"),
44+
name = "secret-key-password",
45+
description = "The PGP secret key password, used for signing.",
46+
hidden = true
47+
)
48+
val pgpPublicKey = new Key.PasswordEntry(
49+
prefix = Seq("pgp"),
50+
name = "public-key",
51+
description = "The PGP public key, used for signing.",
52+
hidden = true
53+
)
1954

20-
val actions = new Key.BooleanEntry(Seq.empty, "actions")
21-
val interactive = new Key.BooleanEntry(Seq.empty, "interactive")
22-
val power = new Key.BooleanEntry(Seq.empty, "power")
55+
val actions = new Key.BooleanEntry(
56+
prefix = Seq.empty,
57+
name = "actions",
58+
description = "Globally enables actionable diagnostics. Enabled by default."
59+
)
60+
val interactive = new Key.BooleanEntry(
61+
prefix = Seq.empty,
62+
name = "interactive",
63+
description = "Globally enables interactive mode (the '--interactive' flag)."
64+
)
65+
val power = new Key.BooleanEntry(
66+
prefix = Seq.empty,
67+
name = "power",
68+
description = "Globally enables power mode (the '--power' launcher flag)."
69+
)
2370

2471
val suppressDirectivesInMultipleFilesWarning =
25-
new Key.BooleanEntry(Seq("suppress-warning"), "directives-in-multiple-files")
72+
new Key.BooleanEntry(
73+
prefix = Seq("suppress-warning"),
74+
name = "directives-in-multiple-files",
75+
description =
76+
"Globally suppresses warnings about directives declared in multiple source files."
77+
)
2678
val suppressOutdatedDependenciessWarning =
27-
new Key.BooleanEntry(Seq("suppress-warning"), "outdated-dependencies-files")
79+
new Key.BooleanEntry(
80+
prefix = Seq("suppress-warning"),
81+
name = "outdated-dependencies-files",
82+
description = "Globally suppresses warnings about outdated dependencies."
83+
)
2884

29-
val proxyAddress = new Key.StringEntry(Seq("httpProxy"), "address")
30-
val proxyUser = new Key.PasswordEntry(Seq("httpProxy"), "user")
31-
val proxyPassword = new Key.PasswordEntry(Seq("httpProxy"), "password")
85+
val proxyAddress = new Key.StringEntry(
86+
prefix = Seq("httpProxy"),
87+
name = "address",
88+
description = "HTTP proxy address.",
89+
hidden = true
90+
)
91+
val proxyUser = new Key.PasswordEntry(
92+
prefix = Seq("httpProxy"),
93+
name = "user",
94+
description = "HTTP proxy user (used for authentication).",
95+
hidden = true
96+
)
97+
val proxyPassword = new Key.PasswordEntry(
98+
prefix = Seq("httpProxy"),
99+
name = "password",
100+
description = "HTTP proxy password (used for authentication).",
101+
hidden = true
102+
)
32103

33-
val repositoryMirrors = new Key.StringListEntry(Seq("repositories"), "mirrors")
34-
val defaultRepositories = new Key.StringListEntry(Seq("repositories"), "default")
104+
val repositoryMirrors = new Key.StringListEntry(
105+
prefix = Seq("repositories"),
106+
name = "mirrors",
107+
description =
108+
s"Repository mirrors, syntax: repositories.mirrors maven:*=https://repository.company.com/maven",
109+
hidden = true
110+
)
111+
val defaultRepositories = new Key.StringListEntry(
112+
prefix = Seq("repositories"),
113+
name = "default",
114+
description =
115+
"Default repository, syntax: https://first-repo.company.com https://second-repo.company.com",
116+
hidden = true
117+
)
35118

36119
// Kept for binary compatibility
37120
val repositoriesMirrors = repositoryMirrors
38121

39122
// setting indicating if the global interactive mode was suggested
40-
val globalInteractiveWasSuggested = new Key.BooleanEntry(Seq.empty, "interactive-was-suggested")
123+
val globalInteractiveWasSuggested = new Key.BooleanEntry(
124+
prefix = Seq.empty,
125+
name = "interactive-was-suggested",
126+
description = "Setting indicating if the global interactive mode was already suggested.",
127+
hidden = true
128+
)
41129

42130
def all: Seq[Key[_]] = Seq[Key[_]](
43131
actions,
@@ -46,6 +134,7 @@ object Keys {
46134
globalInteractiveWasSuggested,
47135
interactive,
48136
suppressDirectivesInMultipleFilesWarning,
137+
suppressOutdatedDependenciessWarning,
49138
pgpPublicKey,
50139
pgpSecretKey,
51140
pgpSecretKeyPassword,
@@ -120,6 +209,8 @@ object Keys {
120209

121210
val repositoryCredentials: Key[List[RepositoryCredentials]] =
122211
new Key[List[RepositoryCredentials]] {
212+
override val description: String = "Repository credentials, syntax: value:user value:password"
213+
override val hidden: Boolean = true
123214

124215
private def asJson(credentials: RepositoryCredentials): RepositoryCredentialsAsJson =
125216
RepositoryCredentialsAsJson(
@@ -242,6 +333,9 @@ object Keys {
242333
}
243334

244335
val publishCredentials: Key[List[PublishCredentials]] = new Key[List[PublishCredentials]] {
336+
override val description: String =
337+
"Publishing credentials, syntax: s1.oss.sonatype.org value:user value:password"
338+
override val hidden: Boolean = true
245339

246340
private def asJson(credentials: PublishCredentials): PublishCredentialsAsJson =
247341
PublishCredentialsAsJson(

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

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,40 +4,57 @@ import caseapp.HelpMessage
44

55
import java.util.stream.IntStream
66

7+
import scala.annotation.tailrec
78
import scala.build.internal.util.ConsoleUtils.*
89

910
object ReferenceDocUtils {
1011
extension (s: String) {
11-
def consoleToFence: String =
12-
s
13-
.linesIterator
14-
.fold("") { (acc, line) =>
15-
val maybeOpenFence =
16-
if line.contains(Console.BOLD) then
17-
"""```sh
18-
|""".stripMargin
19-
else if line.contains(ScalaCliConsole.GRAY) then
20-
"""```scala
21-
|""".stripMargin
22-
else ""
23-
val maybeCloseFence =
24-
if line.contains(Console.RESET) then
25-
"""
26-
|```""".stripMargin
27-
else ""
28-
val newLine = s"$maybeOpenFence${line.noConsoleKeys}$maybeCloseFence"
29-
if acc.isEmpty then newLine
30-
else s"""$acc
31-
|$newLine""".stripMargin
32-
}
12+
def consoleToFence: String = {
13+
@tailrec
14+
def consoleToFenceRec(
15+
remainingLines: Seq[String],
16+
fenceOpen: Boolean = false,
17+
acc: String = ""
18+
): String =
19+
remainingLines.headOption match
20+
case None => acc
21+
case Some(line) =>
22+
val openFenceString =
23+
if line.contains(Console.BOLD) then
24+
"""```sh
25+
|""".stripMargin
26+
else if line.contains(ScalaCliConsole.GRAY) then
27+
"""```scala
28+
|""".stripMargin
29+
else ""
30+
val currentFenceOpen = fenceOpen || openFenceString.nonEmpty
31+
val closeFenceString =
32+
if currentFenceOpen && line.contains(Console.RESET) then
33+
"""
34+
|```""".stripMargin
35+
else ""
36+
val newFenceOpen = currentFenceOpen && closeFenceString.isEmpty
37+
val newLine = s"$openFenceString${line.noConsoleKeys}$closeFenceString"
38+
val newAcc =
39+
if acc.isEmpty then newLine
40+
else
41+
s"""$acc
42+
|$newLine""".stripMargin
43+
consoleToFenceRec(remainingLines.tail, newFenceOpen, newAcc)
44+
consoleToFenceRec(s.linesIterator.toSeq)
45+
}
46+
def filterOutHiddenStrings: String =
47+
s.replace(s"${ScalaCliConsole.GRAY}(hidden)${Console.RESET} ", "")
48+
def consoleYellowToMdBullets: String = s.replace(Console.YELLOW, "- ")
49+
def consoleToMarkdown: String = s.filterOutHiddenStrings.consoleYellowToMdBullets.consoleToFence
3350
}
3451
extension (helpMessage: HelpMessage) {
35-
def referenceDocMessage: String = helpMessage.message.consoleToFence.noConsoleKeys
52+
def referenceDocMessage: String = helpMessage.message.consoleToMarkdown
3653
def referenceDocDetailedMessage: String = {
3754
val msg =
3855
if helpMessage.detailedMessage.nonEmpty then helpMessage.detailedMessage
3956
else helpMessage.message
40-
msg.consoleToFence
57+
msg.consoleToMarkdown
4158
}
4259
}
4360

0 commit comments

Comments
 (0)