diff --git a/modules/build/src/main/scala/scala/build/input/Inputs.scala b/modules/build/src/main/scala/scala/build/input/Inputs.scala index 3c9159c92b..e6d73aee51 100644 --- a/modules/build/src/main/scala/scala/build/input/Inputs.scala +++ b/modules/build/src/main/scala/scala/build/input/Inputs.scala @@ -210,7 +210,8 @@ object Inputs { def validateSnippets( scriptSnippetList: List[String] = List.empty, scalaSnippetList: List[String] = List.empty, - javaSnippetList: List[String] = List.empty + javaSnippetList: List[String] = List.empty, + markdownSnippetList: List[String] = List.empty ): Seq[Either[String, Seq[Element]]] = { def validateSnippet( snippetList: List[String], @@ -236,6 +237,15 @@ object Inputs { javaSnippetList, (content, snippetNameSuffix) => VirtualJavaFile(content, s"-java-$snippetNameSuffix") + ), + validateSnippet( + markdownSnippetList, + (content, snippetNameSuffix) => + VirtualMarkdownFile( + content, + s"-markdown-$snippetNameSuffix", + os.sub / s"$snippetNameSuffix.md" + ) ) ).flatten } @@ -299,6 +309,7 @@ object Inputs { scriptSnippetList: List[String], scalaSnippetList: List[String], javaSnippetList: List[String], + markdownSnippetList: List[String], acceptFds: Boolean, forcedWorkspace: Option[os.Path], enableMarkdown: Boolean, @@ -308,7 +319,7 @@ object Inputs { val validatedArgs: Seq[Either[String, Seq[Element]]] = validateArgs(args, cwd, download, stdinOpt, acceptFds, enableMarkdown) val validatedSnippets: Seq[Either[String, Seq[Element]]] = - validateSnippets(scriptSnippetList, scalaSnippetList, javaSnippetList) + validateSnippets(scriptSnippetList, scalaSnippetList, javaSnippetList, markdownSnippetList) val validatedArgsAndSnippets = validatedArgs ++ validatedSnippets val invalid = validatedArgsAndSnippets.collect { case Left(msg) => msg @@ -342,6 +353,7 @@ object Inputs { scriptSnippetList: List[String] = List.empty, scalaSnippetList: List[String] = List.empty, javaSnippetList: List[String] = List.empty, + markdownSnippetList: List[String] = List.empty, acceptFds: Boolean = false, forcedWorkspace: Option[os.Path] = None, enableMarkdown: Boolean = false, @@ -349,7 +361,8 @@ object Inputs { extraClasspathWasPassed: Boolean ): Either[BuildException, Inputs] = if ( - args.isEmpty && scriptSnippetList.isEmpty && scalaSnippetList.isEmpty && javaSnippetList.isEmpty && !extraClasspathWasPassed + args.isEmpty && scriptSnippetList.isEmpty && scalaSnippetList.isEmpty && javaSnippetList.isEmpty && + markdownSnippetList.isEmpty && !extraClasspathWasPassed ) defaultInputs().toRight(new InputsException( "No inputs provided (expected files with .scala, .sc, .java or .md extensions, and / or directories)." @@ -364,6 +377,7 @@ object Inputs { scriptSnippetList, scalaSnippetList, javaSnippetList, + markdownSnippetList, acceptFds, forcedWorkspace, enableMarkdown, diff --git a/modules/cli-options/src/main/scala/scala/cli/commands/SnippetOptions.scala b/modules/cli-options/src/main/scala/scala/cli/commands/SnippetOptions.scala index 1c8831e92c..07cba0a2d3 100644 --- a/modules/cli-options/src/main/scala/scala/cli/commands/SnippetOptions.scala +++ b/modules/cli-options/src/main/scala/scala/cli/commands/SnippetOptions.scala @@ -32,6 +32,17 @@ final case class SnippetOptions( @Group("Java") @HelpMessage("A synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly") executeJava: List[String] = List.empty, + + @Group("Markdown") + @HelpMessage("[experimental] Allows to execute a passed string as Markdown code") + @Name("mdSnippet") + markdownSnippet: List[String] = List.empty, + + @Group("Markdown") + @HelpMessage("[experimental] A synonym to --markdown-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly") + @Name("executeMd") + @Hidden + executeMarkdown: List[String] = List.empty, ) // format: on diff --git a/modules/cli/src/main/scala/scala/cli/commands/default/Default.scala b/modules/cli/src/main/scala/scala/cli/commands/default/Default.scala index fd87839371..51f039aec5 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/default/Default.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/default/Default.scala @@ -40,6 +40,7 @@ class Default( val shouldDefaultToRun = args.remaining.nonEmpty || options.shared.snippet.executeScript.nonEmpty || options.shared.snippet.executeScala.nonEmpty || options.shared.snippet.executeJava.nonEmpty || + options.shared.snippet.executeMarkdown.nonEmpty || (options.shared.extraJarsAndClassPath.nonEmpty && options.sharedRun.mainClass.mainClass.nonEmpty) if shouldDefaultToRun then RunOptions.parser else ReplOptions.parser }.parse(rawArgs) match diff --git a/modules/cli/src/main/scala/scala/cli/commands/publish/PublishSetup.scala b/modules/cli/src/main/scala/scala/cli/commands/publish/PublishSetup.scala index ff5e383abf..7394bc75b3 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/publish/PublishSetup.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/publish/PublishSetup.scala @@ -56,6 +56,7 @@ object PublishSetup extends ScalaCommand[PublishSetupOptions] { options.input.forbid, Nil, Nil, + Nil, Nil ) maybeInputs match { diff --git a/modules/cli/src/main/scala/scala/cli/commands/util/SharedOptionsUtil.scala b/modules/cli/src/main/scala/scala/cli/commands/util/SharedOptionsUtil.scala index 99f81419a8..b3e862374c 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/util/SharedOptionsUtil.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/util/SharedOptionsUtil.scala @@ -60,6 +60,7 @@ object SharedOptionsUtil extends CommandHelpers { scriptSnippetList: List[String], scalaSnippetList: List[String], javaSnippetList: List[String], + markdownSnippetList: List[String], enableMarkdown: Boolean = false, extraClasspathWasPassed: Boolean = false ): Either[BuildException, Inputs] = { @@ -80,6 +81,7 @@ object SharedOptionsUtil extends CommandHelpers { scriptSnippetList = scriptSnippetList, scalaSnippetList = scalaSnippetList, javaSnippetList = javaSnippetList, + markdownSnippetList = markdownSnippetList, acceptFds = !Properties.isWin, forcedWorkspace = forcedWorkspaceOpt, enableMarkdown = enableMarkdown, @@ -407,13 +409,15 @@ object SharedOptionsUtil extends CommandHelpers { scriptSnippetList = allScriptSnippets, scalaSnippetList = allScalaSnippets, javaSnippetList = allJavaSnippets, + markdownSnippetList = allMarkdownSnippets, enableMarkdown = v.markdown.enableMarkdown, extraClasspathWasPassed = v.extraJarsAndClassPath.nonEmpty ) - def allScriptSnippets: List[String] = v.snippet.scriptSnippet ++ v.snippet.executeScript - def allScalaSnippets: List[String] = v.snippet.scalaSnippet ++ v.snippet.executeScala - def allJavaSnippets: List[String] = v.snippet.javaSnippet ++ v.snippet.executeJava + def allScriptSnippets: List[String] = v.snippet.scriptSnippet ++ v.snippet.executeScript + def allScalaSnippets: List[String] = v.snippet.scalaSnippet ++ v.snippet.executeScala + def allJavaSnippets: List[String] = v.snippet.javaSnippet ++ v.snippet.executeJava + def allMarkdownSnippets: List[String] = v.snippet.markdownSnippet ++ v.snippet.executeMarkdown def validateInputArgs(args: Seq[String]): Seq[Either[String, Seq[Element]]] = Inputs.validateArgs( diff --git a/modules/integration/src/test/scala/scala/cli/integration/DefaultTests.scala b/modules/integration/src/test/scala/scala/cli/integration/DefaultTests.scala index e807b9a280..bb17c3ea76 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/DefaultTests.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/DefaultTests.scala @@ -77,6 +77,26 @@ class DefaultTests extends ScalaCliSuite { } } + test("default to the run sub-command when a md snippet is passed with --execute-markdown") { + TestInputs.empty.fromRoot { root => + val msg = "Hello world" + val quotation = TestUtil.argQuotationMark + val res = + os.proc( + TestUtil.cli, + "--execute-markdown", + s"""# A Markdown snippet + |With some scala code + |```scala + |println($quotation$msg$quotation) + |```""".stripMargin, + TestUtil.extraOptions + ) + .call(cwd = root) + expect(res.out.trim() == msg) + } + } + test("running scala-cli with a script snippet passed with -e shouldn't allow repl-only options") { TestInputs.empty.fromRoot { root => val replSpecificOption = "--repl-dry-run" diff --git a/modules/integration/src/test/scala/scala/cli/integration/RunSnippetTestDefinitions.scala b/modules/integration/src/test/scala/scala/cli/integration/RunSnippetTestDefinitions.scala index 4745fc6e6d..e894168403 100644 --- a/modules/integration/src/test/scala/scala/cli/integration/RunSnippetTestDefinitions.scala +++ b/modules/integration/src/test/scala/scala/cli/integration/RunSnippetTestDefinitions.scala @@ -53,6 +53,27 @@ trait RunSnippetTestDefinitions { _: RunTestDefinitions => } } + test("correctly run a markdown snippet") { + emptyInputs.fromRoot { root => + val msg = "Hello world" + val quotation = TestUtil.argQuotationMark + val res = + os.proc( + TestUtil.cli, + "run", + "--markdown-snippet", + s"""# A Markdown snippet + |With some scala code + |```scala + |println($quotation$msg$quotation) + |```""".stripMargin, + extraOptions + ) + .call(cwd = root) + expect(res.out.trim() == msg) + } + } + test("correctly run multiple snippets") { emptyInputs.fromRoot { root => val quotation = TestUtil.argQuotationMark diff --git a/website/docs/guides/snippets.md b/website/docs/guides/snippets.md index 4f6c3c6b3d..f2445208c8 100644 --- a/website/docs/guides/snippets.md +++ b/website/docs/guides/snippets.md @@ -80,6 +80,24 @@ Hello +- Markdown code (experimental) + + + +````bash +scala-cli run --markdown-snippet '# Markdown snippet +with a code block +```scala +println("Hello") +```' +```` + +```text +Hello +``` + + + - a mix of Scala, Java and scripts diff --git a/website/docs/reference/cli-options.md b/website/docs/reference/cli-options.md index 5566d36b42..4aea1c0c41 100644 --- a/website/docs/reference/cli-options.md +++ b/website/docs/reference/cli-options.md @@ -1399,6 +1399,19 @@ Allows to execute a passed string as Java code A synonym to --scala-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly +### `--markdown-snippet` + +Aliases: `--md-snippet` + +[experimental] Allows to execute a passed string as Markdown code + +### `--execute-markdown` + +Aliases: `--execute-md` + +[Internal] +[experimental] A synonym to --markdown-snippet, which defaults the sub-command to `run` when no sub-command is passed explicitly + ## Test options Available in commands: