@@ -5,17 +5,18 @@ import fansi.Color.{Blue, Green, Red}
5
5
import java .io .File
6
6
import java .security .SecureRandom
7
7
8
+ import scala .annotation .tailrec
8
9
import scala .io .StdIn .readLine
9
10
import scala .util .Random
10
11
import scala .util .matching .Regex
11
12
12
- val SnippetBlock = """ *``` [^ ]+ title=([\w\d\ .\-\ /_]+) *""" .r
13
- val CompileBlock = """ *``` *(\w+) +(compile|fail) *(?:title=([\w\d\ .\-\ /_]+))? *""" .r
14
- val CodeBlockEnds = """ *``` *""" .r
15
- val BashCommand = """ *```bash *(fail)? *""" .r
16
- val CheckBlock = """ *\<\!-- Expected(-regex)?: *""" .r
17
- val CheckBlockEnd = """ *\--> *""" .r
18
- val Clear = """ *<!--+ *clear *-+-> *""" .r
13
+ val SnippetBlock = """ *(`{2}`+) [^ ]+ title=([\w\d.\-/_]+) *""" .r
14
+ val CompileBlock = """ *(`{2}`+) *(\w+) +(compile|fail) *(?:title=([\w\d.\-/_]+))? *""" .r
15
+ def compileBlockEnds ( backticks : String ) = s """ * $backticks * """ .r
16
+ val BashCommand = """ *```bash *(fail)? *""" .r
17
+ val CheckBlock = """ *\<\!-- Expected(-regex)?: *""" .r
18
+ val CheckBlockEnd = """ *\--> *""" .r
19
+ val Clear = """ *<!--+ *clear *-+-> *""" .r
19
20
20
21
case class Options (
21
22
scalaCliCommand : Seq [String ],
@@ -28,8 +29,8 @@ case class Options(
28
29
29
30
enum Commands :
30
31
def context : Context
31
- def name = toString.takeWhile(_ != '(' )
32
- def log = this match {
32
+ def name : String = toString.takeWhile(_ != '(' )
33
+ def log : Any = this match {
33
34
case _ : Clear => " "
34
35
case Check (patterns, regex, _) =>
35
36
val kind = if regex then " regexes" else " patterns"
@@ -51,13 +52,13 @@ enum Commands:
51
52
case Clear (context : Context )
52
53
53
54
case class Context (file : os.RelPath , line : Int ):
54
- def proceed (linesToSkip : Int = 1 ) = copy(line = line + linesToSkip)
55
- override def toString = s " $file: $line"
55
+ def proceed (linesToSkip : Int = 1 ): Context = copy(line = line + linesToSkip)
56
+ override def toString = s " $file: $line"
56
57
57
58
case class FailedCheck (line : Int , file : os.RelPath , txt : String )
58
59
extends RuntimeException (s " [ $file: $line] $txt" )
59
60
60
- def check (cond : Boolean , msg : => String )(using c : Context ) =
61
+ def check (cond : Boolean , msg : => String )(using c : Context ): Unit =
61
62
if ! cond then throw FailedCheck (c.line, c.file, msg)
62
63
63
64
@ annotation.tailrec
@@ -67,7 +68,7 @@ def parse(content: Seq[String], currentCommands: Seq[Commands], context: Context
67
68
inline def parseMultiline (
68
69
lines : Seq [String ],
69
70
newCommand : Seq [String ] => Commands ,
70
- endMarker : Regex = CodeBlockEnds
71
+ endMarker : Regex = compileBlockEnds( " ``` " )
71
72
) =
72
73
val codeLines = lines.takeWhile(l => ! endMarker.matches(l))
73
74
check(codeLines.size > 0 , " Block cannot be empty!" )
@@ -81,12 +82,17 @@ def parse(content: Seq[String], currentCommands: Seq[Commands], context: Context
81
82
content match
82
83
case Nil => currentCommands
83
84
84
- case SnippetBlock (name) :: tail =>
85
- parseMultiline(tail, Commands .Write (name, _, context))
85
+ case SnippetBlock (backticks, name) :: tail =>
86
+ parseMultiline(tail, Commands .Write (name, _, context), compileBlockEnds(backticks) )
86
87
87
- case CompileBlock (name, status, fileName) :: tail =>
88
- val file = Option (fileName).getOrElse(" snippet_" + Random .nextInt(1000 ) + " ." + name)
89
- parseMultiline(tail, Commands .Compile (file, _, context, status == " fail" ))
88
+ case CompileBlock (backticks, name, status, fileName) :: tail =>
89
+ val fileSuffix = if name == " markdown" then " .md" else s " . $name"
90
+ val file = Option (fileName).getOrElse(" snippet_" + Random .nextInt(1000 ) + fileSuffix)
91
+ parseMultiline(
92
+ tail,
93
+ Commands .Compile (file, _, context, status == " fail" ),
94
+ compileBlockEnds(backticks)
95
+ )
90
96
91
97
case BashCommand (failGroup) :: tail =>
92
98
parseMultiline(tail, Commands .Run (_, failGroup != null , context))
@@ -192,10 +198,10 @@ def checkFile(file: os.Path, options: Options): Unit =
192
198
var lastOutput : String = null
193
199
val allSources = Set .newBuilder[os.Path ]
194
200
195
- def runCommand (cmd : Commands , log : String => Unit ) =
201
+ def runCommand (cmd : Commands , log : String => Unit ): Unit =
196
202
given Context = cmd.context
197
203
198
- def writeFile (file : os.Path , code : Seq [String ], c : Context ) =
204
+ def writeFile (file : os.Path , code : Seq [String ], c : Context ): Unit =
199
205
val (prefixLines, codeLines) =
200
206
code match
201
207
case shbang :: tail if shbang.startsWith(" #!" ) =>
@@ -249,7 +255,7 @@ def checkFile(file: os.Path, options: Options): Unit =
249
255
else
250
256
check(exitCode == 0 , s " Compilation failed. " )
251
257
252
- case Commands .Check (patterns, regex, line ) =>
258
+ case Commands .Check (patterns, regex, _ ) =>
253
259
check(lastOutput != null , " No output stored from previous commands" )
254
260
val lines = lastOutput.linesIterator.toList
255
261
@@ -277,7 +283,7 @@ def checkFile(file: os.Path, options: Options): Unit =
277
283
commands.foreach { cmd =>
278
284
val logs = List .newBuilder[String ]
279
285
280
- def printResult (success : Boolean , startTime : Long ) =
286
+ def printResult (success : Boolean , startTime : Long ): Unit =
281
287
val duration = System .currentTimeMillis - startTime
282
288
val commandName = s " [ ${cmd.name} in $duration ms] "
283
289
val cmdLog =
@@ -336,8 +342,8 @@ def checkFile(file: os.Path, options: Options): Unit =
336
342
337
343
os.list(out).filter(_.last.endsWith(" .scala" )).foreach(p => os.copy.into(p, exampleDir))
338
344
339
- @ main def check (args : String * ) =
340
- def processFiles (options : Options ) =
345
+ @ main def check (args : String * ): Unit =
346
+ def processFiles (options : Options ): Unit =
341
347
val paths = options.files.map { str =>
342
348
val path = os.Path (str, os.pwd)
343
349
assert(os.exists(path), s " Provided path $str does not exists in ${os.pwd}" )
@@ -377,6 +383,7 @@ def checkFile(file: os.Path, options: Options): Unit =
377
383
val Dest = PathParameter (" --dest" )
378
384
val StatusFile = PathParameter (" --status-file" )
379
385
386
+ @ tailrec
380
387
def parseArgs (args : Seq [String ], options : Options ): Options = args match
381
388
case Nil => options
382
389
case " --step" :: rest =>
0 commit comments