Skip to content

Commit

Permalink
Merge pull request #1874 from Gedochao/adjust-reference-docs
Browse files Browse the repository at this point in the history
Ensure no console-syntax in reference docs and no `md` fenced blocks in `--help`
  • Loading branch information
Gedochao committed Feb 27, 2023
2 parents 7fa41b9 + b39076b commit c929892
Show file tree
Hide file tree
Showing 16 changed files with 525 additions and 139 deletions.
Expand Up @@ -7,9 +7,10 @@ import java.net.URI
import java.nio.file.Paths

import scala.build.errors.Severity
import scala.build.internal.util.ConsoleUtils.ScalaCliConsole
import scala.build.options.Scope
import scala.collection.mutable
import scala.jdk.CollectionConverters._
import scala.jdk.CollectionConverters.*

class ConsoleBloopBuildClient(
logger: Logger,
Expand Down Expand Up @@ -147,7 +148,7 @@ class ConsoleBloopBuildClient(
}

object ConsoleBloopBuildClient {
private val gray = "\u001b[90m"
private val gray = ScalaCliConsole.GRAY
private val reset = Console.RESET
private val red = Console.RED
private val yellow = Console.YELLOW
Expand Down
@@ -0,0 +1,22 @@
package scala.build.internal.util

object ConsoleUtils {
import Console.*

object ScalaCliConsole {
val GRAY: String = "\u001b[90m"
}

val ansiFormattingKeys: Set[String] = Set(RESET, BOLD, UNDERLINED, REVERSED, INVISIBLE)
val ansiColors: Set[String] =
Set(BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, ScalaCliConsole.GRAY)
val ansiBoldColors: Set[String] =
Set(BLACK_B, RED_B, GREEN_B, YELLOW_B, BLUE_B, MAGENTA_B, CYAN_B, WHITE_B)
val allAnsiColors: Set[String] = ansiColors ++ ansiBoldColors
val allConsoleKeys: Set[String] = allAnsiColors ++ ansiFormattingKeys

extension (s: String) {
def noConsoleKeys: String =
allConsoleKeys.fold(s)((acc, consoleKey) => acc.replace(consoleKey, ""))
}
}
@@ -1,6 +1,7 @@
package scala.cli.commands

import scala.annotation.tailrec
import scala.build.internal.util.ConsoleUtils.ScalaCliConsole

object WatchUtil {

Expand All @@ -15,7 +16,7 @@ object WatchUtil {
}

private def gray(message: String): String = {
val gray = "\u001b[90m"
val gray = ScalaCliConsole.GRAY
val reset = Console.RESET
s"$gray$message$reset"
}
Expand Down
Expand Up @@ -96,9 +96,9 @@ object ConfigOptions {
s"""$helpHeader
|
|Syntax:
| $progName $cmdName key value
| ${Console.BOLD}$progName $cmdName key value${Console.RESET}
|For example, to globally set the interactive mode:
| $progName $cmdName interactive true
| ${Console.BOLD}$progName $cmdName interactive true${Console.RESET}
|
|${HelpMessages.commandDocWebsiteReference(websiteSuffix)}""".stripMargin
}
Expand Up @@ -9,7 +9,7 @@ import scala.cli.commands.tags
// format: off
@HelpMessage(
s"""Creates or updates a GitHub repository secret.
| $progName --power github secret create --repo repo-org/repo-name SECRET_VALUE=value:secret""".stripMargin
| ${Console.BOLD}$progName --power github secret create --repo repo-org/repo-name SECRET_VALUE=value:secret${Console.RESET}""".stripMargin
)
final case class SecretCreateOptions(
@Recurse
Expand Down
3 changes: 2 additions & 1 deletion modules/cli/src/main/scala/scala/cli/commands/run/Run.scala
Expand Up @@ -12,6 +12,7 @@ import scala.build.EitherCps.{either, value}
import scala.build.*
import scala.build.errors.BuildException
import scala.build.input.{Inputs, ScalaCliInvokeData}
import scala.build.internal.util.ConsoleUtils.ScalaCliConsole
import scala.build.internal.{Constants, Runner, ScalaJsLinkerConfig}
import scala.build.options.{BuildOptions, JavaOpt, Platform, ScalacOpt}
import scala.cli.CurrentParams
Expand Down Expand Up @@ -165,7 +166,7 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
(retCode, allowTerminate) match {
case (0, true) =>
case (0, false) =>
val gray = "\u001b[90m"
val gray = ScalaCliConsole.GRAY
val reset = Console.RESET
System.err.println(s"${gray}Program exited with return code $retCode.$reset")
case (_, true) =>
Expand Down
Expand Up @@ -34,10 +34,7 @@ object RunOptions {
|${HelpMessages.acceptedInputs}
|
|To pass arguments to the actual application, just add them after `--`, like:
|
|```sh
|${ScalaCli.progName} run Main.scala AnotherSource.scala -- first-arg second-arg
|```
| ${Console.BOLD}${ScalaCli.progName} run Main.scala AnotherSource.scala -- first-arg second-arg${Console.RESET}
|
|${HelpMessages.commandDocWebsiteReference(cmdName)}""".stripMargin
}
Expand Up @@ -37,7 +37,7 @@ object SetupIdeOptions {
|It is also ran implicitly when `compile`, `run`, `shebang` or `test` sub-commands are called.
|
|The pre-configuration should be saved in a BSP json connection file under the path:
| {project-root}/.bsp/$baseRunnerName.json
| ${Console.BOLD}{project-root}/.bsp/$baseRunnerName.json${Console.RESET}
|
|${HelpMessages.commandConfigurations(cmdName)}
|
Expand Down
Expand Up @@ -27,7 +27,7 @@ object HelpMessages {
def commandFullHelpReference(commandName: String, needsPower: Boolean = false): String = {
val maybePowerString = if needsPower then "--power " else ""
s"""You are currently viewing the basic help for the $commandName sub-command. You can view the full help by running:
| ${ScalaCli.progName} $maybePowerString$commandName --help-full""".stripMargin
| ${Console.BOLD}${ScalaCli.progName} $maybePowerString$commandName --help-full${Console.RESET}""".stripMargin
}

def commandDocWebsiteReference(websiteSuffix: String): String =
Expand Down
Expand Up @@ -2,6 +2,7 @@ package scala.cli.commands.shebang

import caseapp.*

import scala.build.internal.util.ConsoleUtils.ScalaCliConsole
import scala.cli.ScalaCli.{baseRunnerName, fullRunnerName, progName}
import scala.cli.commands.run.RunOptions
import scala.cli.commands.shared.{HasSharedOptions, HelpMessages, SharedOptions}
Expand Down Expand Up @@ -30,23 +31,16 @@ object ShebangOptions {
|
|When relying on the `run` sub-command, inputs and $baseRunnerName options can be mixed,
|while program args have to be specified after `--`
|
|```sh
|$progName [command] [${baseRunnerName}_options | input]... -- [program_arguments]...
|```
| ${Console.BOLD}$progName [command] [${baseRunnerName}_options | input]... -- [program_arguments]...${Console.RESET}
|
|However, for the `shebang` sub-command, only a single input file can be set, while all $baseRunnerName options
|have to be set before the input file.
|All inputs after the first are treated as program arguments, without the need for `--`
|```sh
|$progName shebang [${baseRunnerName}_options]... input [program_arguments]...
|```
| ${Console.BOLD}$progName shebang [${baseRunnerName}_options]... input [program_arguments]...${Console.RESET}
|
|Using this, it is possible to conveniently set up Unix shebang scripts. For example:
|```sh
|#!/usr/bin/env -S $progName shebang --scala-version 2.13
|println("Hello, world")
|```
| ${ScalaCliConsole.GRAY}#!/usr/bin/env -S $progName shebang --scala-version 2.13
| println("Hello, world")${Console.RESET}
|
|${HelpMessages.commandDocWebsiteReference(cmdName)}""".stripMargin
}
Expand Up @@ -9,6 +9,7 @@ import scala.build.EitherCps.{either, value}
import scala.build.Ops.*
import scala.build.*
import scala.build.errors.{BuildException, CompositeBuildException}
import scala.build.internal.util.ConsoleUtils.ScalaCliConsole
import scala.build.internal.{Constants, Runner}
import scala.build.options.{BuildOptions, JavaOpt, Platform, Scope}
import scala.build.testrunner.AsmTestRunner
Expand All @@ -30,7 +31,7 @@ object Test extends ScalaCommand[TestOptions] {
override def helpFormat: HelpFormat =
super.helpFormat.withPrimaryGroups(Seq(HelpGroup.Test, HelpGroup.Watch))

private def gray = "\u001b[90m"
private def gray = ScalaCliConsole.GRAY
private def reset = Console.RESET

override def buildOptions(opts: TestOptions): Option[BuildOptions] = Some {
Expand Down
Expand Up @@ -9,19 +9,20 @@ import munit.internal.difflib.Diff
import shapeless.tag

import java.nio.charset.StandardCharsets
import java.util.{Arrays, Locale}
import java.util

import scala.build.options.{BuildOptions, BuildRequirements}
import scala.build.preprocessing.ScalaPreprocessor
import scala.build.preprocessing.directives.DirectiveHandler
import scala.cli.commands.{ScalaCommand, SpecificationLevel, tags}
import scala.cli.doc.ReferenceDocUtils.*
import scala.cli.util.ArgHelpers.*
import scala.cli.{ScalaCli, ScalaCliCommands}

object GenerateReferenceDoc extends CaseApp[InternalDocOptions] {

implicit class PBUtils(sb: StringBuilder) {
def section(t: String*) =
def section(t: String*): StringBuilder =
sb.append(t.mkString("", "\n", "\n\n"))
}

Expand Down Expand Up @@ -58,7 +59,7 @@ object GenerateReferenceDoc extends CaseApp[InternalDocOptions] {
val content0 = content.getBytes(StandardCharsets.UTF_8)
val needsUpdate = !os.exists(dest) || {
val currentContent = os.read.bytes(dest)
!Arrays.equals(content0, currentContent)
!util.Arrays.equals(content0, currentContent)
}
if (needsUpdate) {
os.write.over(dest, content0, createFolders = true)
Expand Down Expand Up @@ -205,7 +206,7 @@ object GenerateReferenceDoc extends CaseApp[InternalDocOptions] {
b.section(s"`${arg.level.md}` per Scala Runner specification")
else if (isInternal || arg.noHelp) b.append("[Internal]\n")

for (desc <- arg.helpMessage.map(_.message))
for (desc <- arg.helpMessage.map(_.referenceDocMessage))
b.append(
s"""$desc
|
Expand All @@ -222,11 +223,8 @@ object GenerateReferenceDoc extends CaseApp[InternalDocOptions] {

private def optionsReference(
commands: Seq[Command[_]],
allArgs: Seq[Arg],
nameFormatter: Formatter[Name]
): String = {
val argsToShow = allArgs.filterNot(_.isExperimentalOrRestricted)

val b = new StringBuilder

b.section(
Expand All @@ -253,7 +251,7 @@ object GenerateReferenceDoc extends CaseApp[InternalDocOptions] {

b.section(scalacOptionForwarding)

def optionsForCommand(command: Command[_]) = {
def optionsForCommand(command: Command[_]): Unit = {
val supportedArgs = actualHelp(command).args
val argsByLevel = supportedArgs.groupBy(_.level)

Expand All @@ -272,7 +270,7 @@ object GenerateReferenceDoc extends CaseApp[InternalDocOptions] {
args.foreach { arg =>
val names = (arg.name +: arg.extraNames).map(_.option(nameFormatter))
b.section(s"**${names.head}**")
b.section(arg.helpMessage.fold("")(_.message))
b.section(arg.helpMessage.fold("")(_.referenceDocMessage))
if (names.tail.nonEmpty) b.section(names.tail.mkString("Aliases: `", "` ,`", "`"))

}
Expand All @@ -295,7 +293,8 @@ object GenerateReferenceDoc extends CaseApp[InternalDocOptions] {

if (command.names.tail.nonEmpty)
b.section(command.names.map(_.mkString(" ")).tail.mkString("Aliases: `", "`, `", "`"))
for (desc <- command.messages.helpMessage.map(_.message)) b.section(desc)
for (desc <- command.messages.helpMessage.map(_.referenceDocDetailedMessage))
b.section(desc)
optionsForCommand(command)
b.section("---")
}
Expand Down Expand Up @@ -335,14 +334,14 @@ object GenerateReferenceDoc extends CaseApp[InternalDocOptions] {
b.append(s"$headerPrefix## ${names.head}\n\n")
if (names.tail.nonEmpty) b.append(names.tail.sorted.mkString("Aliases: `", "`, `", "`\n\n"))

for (desc <- c.messages.helpMessage.map(_.message)) b.section(desc)
for (desc <- c.messages.helpMessage.map(_.referenceDocDetailedMessage)) b.section(desc)

if (origins.nonEmpty) {
val links = origins.map { origin =>
val cleanedUp = formatOrigin(origin, keepCapitalization = false)
val linkPart = cleanedUp
.split("\\s+")
.map(_.toLowerCase(Locale.ROOT).filter(_ != '.'))
.map(_.toLowerCase(util.Locale.ROOT).filter(_ != '.'))
.mkString("-")
s"[$cleanedUp](./cli-options.md#$linkPart-options)"
}
Expand Down Expand Up @@ -501,7 +500,7 @@ object GenerateReferenceDoc extends CaseApp[InternalDocOptions] {
val allCommandsContent = commandsContent(commands, onlyRestricted = false)
val restrictedCommandsContent = commandsContent(restrictedCommands, onlyRestricted = true)

val scalaOptionsReference = optionsReference(restrictedCommands, allArgs, nameFormatter)
val scalaOptionsReference = optionsReference(restrictedCommands, nameFormatter)

val allDirectivesContent = usingContent(
ScalaPreprocessor.usingDirectiveHandlers,
Expand Down
@@ -0,0 +1,44 @@
package scala.cli.doc

import caseapp.HelpMessage

import java.util.stream.IntStream

import scala.build.internal.util.ConsoleUtils.*

object ReferenceDocUtils {
extension (s: String) {
def consoleToFence: String =
s
.linesIterator
.fold("") { (acc, line) =>
val maybeOpenFence =
if line.contains(Console.BOLD) then
"""```sh
|""".stripMargin
else if line.contains(ScalaCliConsole.GRAY) then
"""```scala
|""".stripMargin
else ""
val maybeCloseFence =
if line.contains(Console.RESET) then
"""
|```""".stripMargin
else ""
val newLine = s"$maybeOpenFence${line.noConsoleKeys}$maybeCloseFence"
if acc.isEmpty then newLine
else s"""$acc
|$newLine""".stripMargin
}
}
extension (helpMessage: HelpMessage) {
def referenceDocMessage: String = helpMessage.message.consoleToFence.noConsoleKeys
def referenceDocDetailedMessage: String = {
val msg =
if helpMessage.detailedMessage.nonEmpty then helpMessage.detailedMessage
else helpMessage.message
msg.consoleToFence
}
}

}

0 comments on commit c929892

Please sign in to comment.