@@ -125,6 +125,7 @@ struct TestCase <: AbstractTestCase
125125 code:: TextBlock
126126 pre:: Union{TextBlock,Nothing}
127127 expect:: Union{TextBlock,Nothing}
128+ repl:: Bool
128129end
129130
130131location (test:: TestCase ) = test. loc
@@ -537,29 +538,36 @@ function parsemd!(stack::Vector{TextBlock})
537538 line = pop! (stack)
538539 if isfence (line)
539540 # Extract a fenced block.
541+ isrepl = false
540542 fenceloc = line. loc
541543 lang = strip (line. val[4 : end ])
542544 jlstack = TextBlock[]
543545 while ! isempty (stack) && ! isfence (stack[end ])
544- push! (jlstack, pop! (stack))
546+ block = pop! (stack)
547+ isrepl = isrepl || startswith (block. val, " julia>" )
548+ push! (jlstack, block)
545549 end
546550 if isempty (stack)
547551 push! (suite, BrokenTestCase (fenceloc, " incomplete fenced code block" ))
548552 else
549553 pop! (stack)
550554 if isempty (lang)
551555 reverse! (jlstack)
552- append! (suite, parsejl! (jlstack))
556+ append! (suite, isrepl ? parsejlrepl! (jlstack) : parsejl! (jlstack))
553557 end
554558 end
555559 elseif isindent (line) && ! isblank (line)
556560 # Extract an indented block.
557- jlstack = TextBlock[unindent (line)]
561+ block = unindent (line)
562+ isrepl = startswith (block. val, " julia>" )
563+ jlstack = TextBlock[block]
558564 while ! isempty (stack) && (isindent (stack[end ]) || isblank (stack[end ]))
559- push! (jlstack, unindent (pop! (stack)))
565+ block = unindent (pop! (stack))
566+ isrepl = isrepl || startswith (block. val, " julia> " )
567+ push! (jlstack, block)
560568 end
561569 reverse! (jlstack)
562- append! (suite, parsejl! (jlstack))
570+ append! (suite, isrepl ? parsejlrepl! (jlstack) : parsejl! (jlstack))
563571 elseif isadmonition (line)
564572 # Skip an indented admonition block.
565573 while ! isempty (stack) && (isindent (stack[end ]) || isblank (stack[end ]))
@@ -588,6 +596,78 @@ function parsejl!(stack::Vector{TextBlock})
588596 return suite
589597end
590598
599+ const PROMPT_REGEX = r" ^julia>(?: (.*))?$"
600+ const SOURCE_REGEX = r" ^ (.*)$"
601+
602+ function parsejlrepl! (stack:: Vector{TextBlock} )
603+ reverse! (stack)
604+ code = TextBlock[]
605+ expect = TextBlock[]
606+ buf = IOBuffer ()
607+ processing_prompt = false
608+ function savecode! (loc)
609+ data = consumebuf! (buf)
610+ if data != = nothing
611+ push! (code, TextBlock (loc, strip (data)))
612+ else
613+ push! (code, TextBlock (loc, " " ))
614+ end
615+ end
616+ function saveexpect! (loc)
617+ data = consumebuf! (buf)
618+ if data != = nothing
619+ push! (expect, TextBlock (loc, rstrip (data)))
620+ end
621+ end
622+ while true
623+ line = popfirst! (stack)
624+ prompt = match (PROMPT_REGEX, line. val)
625+ if prompt === nothing
626+ source = match (SOURCE_REGEX, line. val)
627+ if source === nothing
628+ processing_prompt = false
629+ savecode! (line. loc)
630+ println (buf, rstrip (line. val))
631+ takeuntil! (PROMPT_REGEX, buf, stack)
632+ else
633+ processing_prompt = true
634+ println (buf, source[1 ])
635+ end
636+ else
637+ processing_prompt = true
638+ saveexpect! (line. loc)
639+ if prompt[1 ] != = nothing
640+ println (buf, prompt[1 ])
641+ end
642+ end
643+ if isempty (stack)
644+ processing_prompt ? savecode! (line. loc) : saveexpect! (line. loc)
645+ for _ in length (code): length (expect) + 1
646+ push! (expect, TextBlock (line. loc, " " ))
647+ end
648+ break
649+ end
650+ end
651+ [isempty (c. val) ?
652+ BrokenTestCase (c. loc, " empty test case" ) :
653+ TestCase (c. loc, c, nothing , e, true ) for (c, e) in zip (code, expect)]
654+ end
655+
656+ function consumebuf! (buf)
657+ n = bytesavailable (seekstart (buf))
658+ n > 0 ? String (take! (buf)) : nothing
659+ end
660+
661+ function takeuntil! (r, buf, lines:: Vector{TextBlock} )
662+ while ! isempty (lines)
663+ if ! occursin (r, lines[1 ]. val)
664+ println (buf, rstrip (popfirst! (lines). val))
665+ else
666+ break
667+ end
668+ end
669+ end
670+
591671# Extract a test case from Julia source.
592672
593673function parsecase! (stack:: Vector{TextBlock} )
@@ -638,7 +718,7 @@ function parsecase!(stack::Vector{TextBlock})
638718 end
639719 end
640720 ! isempty (code) || return BrokenTestCase (loc, " missing test code" )
641- return TestCase (loc, collapse (code), collapse (pre), collapse (expect))
721+ return TestCase (loc, collapse (code), collapse (pre), collapse (expect), false )
642722end
643723
644724# Run a single test case.
@@ -703,7 +783,11 @@ function runtest(test::TestCase; subs=common_subs(), mod=nothing)
703783 body = asexpr (test. code)
704784 ans = Core. eval (mod, body)
705785 if ans != = nothing && ! no_output
706- Base. invokelatest (show, io, ans)
786+ if test. repl
787+ Base. invokelatest (show, io, " text/plain" , ans)
788+ else
789+ Base. invokelatest (show, io, ans)
790+ end
707791 end
708792 end
709793 catch exc
@@ -746,10 +830,11 @@ end
746830runtest (test:: BrokenTestCase ; subs= common_subs (), mod= nothing ) =
747831 Error (test)
748832
749- runtest (loc, code; pre= nothing , expect= nothing , subs= common_subs (), mod= nothing ) =
833+ runtest (loc, code; pre= nothing , expect= nothing , subs= common_subs (), mod= nothing , repl = false ) =
750834 runtest (TestCase (loc, TextBlock (loc, code),
751835 pre != = nothing ? TextBlock (loc, pre) : nothing ,
752- expect != = nothing ? TextBlock (loc, expect) : nothing ),
836+ expect != = nothing ? TextBlock (loc, expect) : nothing ,
837+ repl),
753838 subs= subs, mod= mod)
754839
755840# Convert expected output block to a regex.
0 commit comments