Skip to content

Commit 8317daf

Browse files
authored
Features and improvements (#28)
* strip prompt, VSCode `show` methods * add some basic tests * add support for named blocks * add named blocks to docs * bump
1 parent 3f2956e commit 8317daf

File tree

9 files changed

+123
-23
lines changed

9 files changed

+123
-23
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
fail-fast: false
3939
matrix:
4040
version:
41-
- '1.7'
41+
- '1.8'
4242
- '1'
4343
os:
4444
- windows-latest

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ StructTypes = "1.7"
3434
Test = "<0.0.1,1"
3535
UUIDs = "<0.0.1,1"
3636
agg_jll = "1.4.3"
37-
julia = "1.7"
37+
julia = "1.8"
3838
pandoc_jll = "3.1.9"
3939

4040
[extras]

docs/src/creating_casts.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,21 @@ cast"""
1313
"""0.25
1414
```
1515

16-
The `Cast` objects have a `show` method defined for HTML, allowing rich display with a local [asciinema-player](https://github.com/asciinema/asciinema-player), in Documenter, Pluto, jupyter, etc. For convenient use with Documenter in particular, see the `@cast` syntax in [Documenter usage](@ref). Note that this player needs the asciinema-player javascript and CSS assets to be loaded.
16+
The `Cast` objects have a `show` method defined for HTML, allowing rich display with a local [asciinema-player](https://github.com/asciinema/asciinema-player), in Documenter, Pluto, jupyter, VSCode, etc. For convenient use with Documenter in particular, see the `@cast` syntax in [Documenter usage](@ref). Note that this player needs the asciinema-player javascript and CSS assets to be loaded (note that in VSCode, this happens automatically).
1717

1818
They can be saved to a `.cast` file using [`Asciicast.save`](@ref) or saved to a gif using [`Asciicast.save_gif`](@ref). See also [Markdown usage](@ref) for easier integration into READMEs and other documents.
1919

20+
Note also that `julia>` prompts may be prepended. In this case, existing outputs will be discarded, similarly to the REPL's prompt-pasting feature:
21+
22+
```@example
23+
using Asciicast
24+
c = cast"""
25+
julia> 1+1
26+
3 # note: wrong!
27+
28+
"""0.25
29+
```
30+
2031
```@docs
2132
@cast_str
2233
```

docs/src/markdown_usage.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,32 @@ Note that the attributes must be separated by spaces, not commas, as shown above
3737

3838
Here, the gifs are generated with [`agg`](https://github.com/asciinema/agg) (which is installed automatically using a JLL package), and the font-size parameter is passed there. Currently no other `agg` parameters are supported, but file an issue if you have a use for one.
3939

40+
### Named blocks
41+
42+
One can name blocks to continue execution after interrupting by some text. For example:
43+
````markdown
44+
Here we have `x`:
45+
```julia {cast="true" name="ex1"}
46+
x=2
47+
48+
```
49+
50+
Now we add 1:
51+
```julia {cast="true" name="ex1"}
52+
y = x+1
53+
54+
```
55+
````
56+
57+
This works the same way as [named example blocks in Documenter](https://documenter.juliadocs.org/stable/man/syntax/#@example-block).
58+
4059
### All supported attributes
4160

4261
* `delay::Float64=0.25`. The amount of delay between line executions (to emulate typing time).
4362
* `font-size::Int=28`. Used by `agg` when generating the gif.
4463
* `height::Int`. Heuristically determined by default. Set to an integer to specify the number of lines.
4564
* `allow_errors::Bool=false`. Whether or not [`cast_document`](@ref) (or [`cast_readme`](@ref)) should fail if exceptions are encountered during execution of the block.
65+
* `name::String`. Optionally provide a name to allow running multiple examples in the same module, similar to named example blocks in Documenter.
4666

4767
### Syntax notes
4868

src/Asciicast.jl

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -143,13 +143,22 @@ end
143143

144144
collect_bytes(cast::Cast) = collect_bytes(cast.write_handle)
145145

146-
function Base.show(io::IO, mime::MIME"text/html", cast::Cast)
147-
base64_str = base64encode(collect_bytes(cast))
146+
Base.show(io::IO, ::MIME"text/html", cast::Cast) = show_html(io, cast)
147+
function Base.show(io::IO, ::MIME"juliavscode/html", cast::Cast)
148+
nodes = Documenter.HTMLWriter.asset_links(".", Asciicast.assets())
149+
for node in nodes
150+
print(io, node)
151+
end
152+
show_html(io, cast)
153+
end
154+
155+
function show_html(io::IO, cast::Cast)
156+
base64_str = base64encode(collect_bytes(cast))
148157
name = uuid4()
149158
# Note: the extra div with `margin` is me trying to make the asciinema player
150159
# have a little space around it, so it looks better in documenter pages etc.
151160
# I don't know what I'm doing; if this is bad, make a PR to improve it!
152-
html = HTML("""
161+
html = """
153162
<div style="margin: 20px">
154163
<div id="$(name)"></div>
155164
<script>
@@ -159,8 +168,8 @@ function Base.show(io::IO, mime::MIME"text/html", cast::Cast)
159168
);
160169
</script>
161170
</div>
162-
""")
163-
return show(io, mime, html)
171+
"""
172+
return print(io, html)
164173
end
165174

166175
"""

src/gif.jl

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
44
Given Julia source code as a string, run the code in a REPL mode and save the results as a gif to `output_path`.
55
"""
6-
function save_code_gif(output_path, code_string; delay=0.25, font_size=28, height=nothing, allow_errors=false)
7-
cast = _cast_str(code_string, delay; height, allow_errors)
6+
function save_code_gif(output_path, code_string; delay=0.25, font_size=28, height=nothing, allow_errors=false, mod=Module())
7+
cast = _cast_str(code_string, delay; height, allow_errors, mod)
88
save_gif(output_path, cast::Cast; font_size)
99
return output_path
1010
end
@@ -33,14 +33,14 @@ function get_attribute(attributes, key, default)
3333
value = tryparse(typeof(default), attributes[idx][2])
3434
if value === nothing
3535
@warn "Invalid $(key) $(attributes[idx][2]). Using default value $default."
36-
value = 28
36+
value = default
3737
end
3838
end
3939
return value
4040
end
4141

4242
# Pandoc filter to add gifs with the contents of `julia {cast="true"}` code blocks.
43-
function cast_action(tag, content, format, meta; base_dir, counter)
43+
function cast_action(tag, content, format, meta; base_dir, counter, module_meta)
4444
tag == "CodeBlock" || return nothing
4545
length(content) < 2 && return nothing
4646
length(content[1]) < 3 && return nothing
@@ -51,16 +51,21 @@ function cast_action(tag, content, format, meta; base_dir, counter)
5151
font_size = get_attribute(attributes, "font-size", 28)
5252
delay = get_attribute(attributes, "delay", 0.25)
5353
height = get_attribute(attributes, "height", 0)
54+
name_idx = findfirst(attr -> attr[1] == "name", attributes)
55+
example_name = isnothing(name_idx) ? nothing : attributes[name_idx][2]
56+
5457
allow_errors = get_attribute(attributes, "allow_errors", false)
5558
if height == 0
5659
height = nothing
5760
end
5861
block = content[2]
5962
counter[] += 1
6063
c = counter[]
61-
name = "output_$(c)[email protected]"
62-
rel_path = joinpath("assets", name)
63-
save_code_gif(joinpath(base_dir, rel_path), block; delay, font_size, height, allow_errors)
64+
rel_path = joinpath("assets", "output_$(c)[email protected]")
65+
66+
mod = Documenter.get_sandbox_module!(module_meta, "@cast", example_name)
67+
68+
save_code_gif(joinpath(base_dir, rel_path), block; delay, font_size, height, allow_errors, mod)
6469
return [
6570
Pandoc.CodeBlock(content...)
6671
Pandoc.Para([Pandoc.Image(["", [], []], [], [rel_path, ""])])
@@ -110,7 +115,9 @@ function cast_document(input_path, output_path=input_path; format="gfm+attribute
110115
base_dir = dirname(output_path)
111116
mkpath(joinpath(base_dir, "assets"))
112117
counter = Ref{Int}(0)
113-
act = (args...) -> cast_action(args...; base_dir, counter)
118+
module_meta = Dict{Symbol, Any}()
119+
120+
act = (args...) -> cast_action(args...; base_dir, counter, module_meta)
114121
output = JSON3.write(Pandoc.filter(json, [rm_old_gif, act]))
115122
open(`$(pandoc()) -f json -t $format --wrap=preserve --resource-path=$(base_dir) -o $output_path`; write=true) do io
116123
write(io, output)

src/runner.jl

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,10 +139,25 @@ function Base.showerror(io::IO, c::CastExecutionException)
139139
""")
140140
end
141141

142-
function cast_from_string!(code_string::AbstractString, cast::Cast; doc=FakeDoc(), page=FakePage(), ansicolor=true, mod=Module(), multicodeblock=MarkdownAST.CodeBlock[], allow_errors=false, x=nothing)
142+
function cast_from_string!(code_string::AbstractString, cast::Cast; doc=FakeDoc(), page=FakePage(), ansicolor=true, mod=Module(), multicodeblock=MarkdownAST.CodeBlock[], allow_errors=false, x=nothing, remove_prompt=false)
143143
linenumbernode = LineNumberNode(0, "REPL") # line unused, set to 0
144144
@debug "Evaluating @cast:\n$(x.code)"
145145

146+
# if there are prompts, and we are to remove them, we will use
147+
# prompt-pasting semantics, where only lines with the prompt count,
148+
# and those prompts are removed.
149+
# xref https://github.com/JuliaLang/julia/pull/17599/files
150+
if remove_prompt && contains(code_string, r"^julia>"m)
151+
code_string_io = IOBuffer()
152+
for line in eachsplit(code_string, r"(?>\r\n|\n|\r)")
153+
startswith(line, "julia>") || continue
154+
n = 6 # length("julia>")
155+
n += startswith(" ", line)
156+
println(code_string_io, @view(line[(n+1):end]))
157+
end
158+
code_string = String(take!(code_string_io))
159+
end
160+
146161
pb = Documenter.parseblock(code_string, doc, page; keywords=false,
147162
linenumbernode=linenumbernode)
148163
n = length(pb)
@@ -240,10 +255,10 @@ macro cast_str(code_string, delay=0, allow_errors=false)
240255
return _cast_str(code_string, delay; allow_errors)
241256
end
242257

243-
function _cast_str(code_string, delay=0; height=nothing, allow_errors=false)
258+
function _cast_str(code_string, delay=0; height=nothing, allow_errors=false, mod=Module())
244259
n_lines = length(split(code_string))
245260
height = something(height, min(n_lines * 2, 24)) # try to choose the number of lines more appropriately
246261
cast = Cast(IOBuffer(), Header(; height); delay=delay)
247-
cast_from_string!(code_string, cast; allow_errors)
262+
cast_from_string!(code_string, cast; allow_errors, remove_prompt=true, mod)
248263
return cast
249264
end

test/runtests.jl

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,15 +118,34 @@ end
118118
tmp = mktempdir()
119119
output = joinpath(tmp, "output.md")
120120
# Check that we can parse these blocks and produce the gif tags.
121-
@test cast_document("test_doc.md", output) == 3
121+
n = 6
122+
@test cast_document("test_doc.md", output) == n
122123
str = read(output, String)
123-
@test contains(str, "[email protected]")
124-
@test contains(str, "output_2_@cast.gif")
125-
@test contains(str, "[email protected]")
124+
for i in 1:n
125+
@test contains(str, "output_$(i)_@cast.gif")
126+
end
126127
end
127128

128129
@testset "markdown errors" begin
129130
tmp = mktempdir()
130131
output = joinpath(tmp, "output.md")
131132
@test_throws CastExecutionException cast_document("bad.md", output)
132133
end
134+
135+
@testset "`Cast` show methods" begin
136+
c = cast"""
137+
julia> 1+1
138+
2
139+
140+
julia> 3
141+
3
142+
"""0.25
143+
vscode_str = sprint(show, MIME"juliavscode/html"(), c)
144+
# Contains the JS and css:
145+
@test contains(vscode_str, "asciinema-player.min.js")
146+
@test contains(vscode_str, "asciinema-player.min.css")
147+
@test contains(vscode_str, "AsciinemaPlayer.create")
148+
149+
html_str = sprint(show, MIME"text/html"(), c)
150+
@test contains(html_str, "AsciinemaPlayer.create")
151+
end

test/test_doc.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,22 @@
99
```julia {cast="true" allow_errors="true"}
1010
error("oops")
1111
```
12+
13+
```julia {cast="true"}
14+
julia> 1+1
15+
2
16+
17+
julia> 3
18+
3
19+
20+
```
21+
22+
```julia {cast="true" name="ex1"}
23+
x=2
24+
25+
```
26+
27+
```julia {cast="true" name="ex1"}
28+
y = x+1
29+
30+
```

0 commit comments

Comments
 (0)