Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support inline code #80

Merged
merged 11 commits into from
Dec 29, 2016
17 changes: 17 additions & 0 deletions doc/src/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,20 @@ Weave will remove the first empty space from each line of documentation.


[See sample document:](https://github.com/mpastell/Weave.jl/blob/master/examples/FIR_design.jl)

## Inline code

You can also add inline code to your documents using

```
`j juliacode`
```

syntax. The code will be replaced with the output of running the code.
If the code produces figures the filename or base64 encoded string will be
added to output e.g. to include a Plots figure in markdown you can use:

```
![A plot](`j plot(1:10)`)
```

71 changes: 37 additions & 34 deletions src/Weave.jl
Original file line number Diff line number Diff line change
Expand Up @@ -87,46 +87,49 @@ function weave(source ; doctype = :auto, plotlib=:auto,
css != nothing && (doc.css = css)
template != nothing && (doc.template = template)

try
doc = run(doc, doctype = doctype, plotlib=plotlib,
out_path=out_path, args = args,
fig_path = fig_path, fig_ext = fig_ext, cache_path = cache_path, cache=cache)
formatted = format(doc)

doc = run(doc, doctype = doctype, plotlib=plotlib,
out_path=out_path, args = args,
fig_path = fig_path, fig_ext = fig_ext, cache_path = cache_path, cache=cache)
formatted = format(doc)
outname = get_outname(out_path, doc)

outname = get_outname(out_path, doc)
open(outname, "w") do io
write(io, formatted)
end

open(outname, "w") do io
write(io, formatted)
end
#Special for that need external programs
if doc.doctype == "pandoc2html"
mdname = outname
outname = get_outname(out_path, doc, ext = "html")
pandoc2html(formatted, doc, outname)
rm(mdname)
elseif doc.doctype == "pandoc2pdf"
mdname = outname
outname = get_outname(out_path, doc, ext = "pdf")
pandoc2pdf(formatted, doc, outname)
rm(mdname)
elseif doc.doctype == "md2pdf"
success = run_latex(doc, outname, latex_cmd)
success || return
outname = get_outname(out_path, doc, ext = "pdf")
end

#Special for that need external programs
if doc.doctype == "pandoc2html"
mdname = outname
outname = get_outname(out_path, doc, ext = "html")
pandoc2html(formatted, doc, outname)
rm(mdname)
elseif doc.doctype == "pandoc2pdf"
mdname = outname
outname = get_outname(out_path, doc, ext = "pdf")
pandoc2pdf(formatted, doc, outname)
rm(mdname)
elseif doc.doctype == "md2pdf"
success = run_latex(doc, outname, latex_cmd)
success || return
outname = get_outname(out_path, doc, ext = "pdf")
end

doctype == :auto && (doctype = detect_doctype(doc.source))

if contains(doctype, "2pdf") && cache == :off
rm(doc.fig_path, force = true, recursive = true)
elseif contains(doctype, "2html")
rm(doc.fig_path, force = true, recursive = true)
doc.cwd == pwd() && (outname = basename(outname))
info("Report weaved to $outname")
catch e
warn("Something went wrong during weaving")
print(e)
finally
doctype == :auto && (doctype = detect_doctype(doc.source))
if contains(doctype, "2pdf") && cache == :off
rm(doc.fig_path, force = true, recursive = true)
elseif contains(doctype, "2html")
rm(doc.fig_path, force = true, recursive = true)
end
end

doc.cwd == pwd() && (outname = basename(outname))

info("Report weaved to $outname")
end

function weave(doc::AbstractString, doctype::AbstractString)
Expand Down
27 changes: 24 additions & 3 deletions src/cache.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,28 @@ function restore_chunk(chunk::CodeChunk, cached)
return new_chunks
end

#Could be used to restore inline code in future
function restore_chunk(chunk::DocChunk, cached)
return chunk
#Restore inline code
function restore_chunk(chunk::DocChunk, cached::WeaveDoc)
#Get chunk from cached doc
c_chunk = filter(x -> x.number == chunk.number &&
isa(x, DocChunk), cached.chunks)
isempty(c_chunk) && return chunk
c_chunk = c_chunk[1]

#Collect cached code
c_inline = filter(x -> isa(x, InlineCode), c_chunk.content)
isempty(c_inline) && return chunk

#Restore cached results for Inline code
n = length(chunk.content)
for i in 1:n
if isa(chunk.content[i], InlineCode)
ci = filter(x -> x.number == chunk.content[i].number, c_inline)
isempty(ci) && continue
chunk.content[i].output = ci[1].output
chunk.content[i].rich_output = ci[1].rich_output
chunk.content[i].figures = ci[1].figures
end
end
return chunk
end
35 changes: 31 additions & 4 deletions src/chunks.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@

abstract WeaveChunk
abstract Inline

type WeaveDoc
source::AbstractString
basename::AbstractString
path::AbstractString
chunks::Array
chunks::Array{WeaveChunk}
cwd::AbstractString
format
doctype::AbstractString
Expand All @@ -29,7 +32,7 @@ immutable ChunkOutput
figures::Array{AbstractString}
end

type CodeChunk
type CodeChunk <: WeaveChunk
content::AbstractString
number::Int
result_no::Int
Expand All @@ -45,10 +48,34 @@ type CodeChunk
end
end

type DocChunk
content::AbstractString
type DocChunk <: WeaveChunk
content::Array{Inline}
number::Int
start_line::Int
function DocChunk(text::AbstractString, number::Int, start_line::Int, inline_regex = nothing)
chunks = parse_inline(text, inline_regex)
new(chunks, number, start_line)
end
end

type InlineText <: Inline
content::AbstractString
si::Int
ei::Int
number::Int
end

type InlineCode <: Inline
content::AbstractString
si::Int
ei::Int
number::Int
output::AbstractString
rich_output::AbstractString
figures::Array{AbstractString}
function InlineCode(content, si, ei, number)
new(content, si, ei, number, "", "", AbstractString[])
end
end

type TermResult
Expand Down
23 changes: 17 additions & 6 deletions src/format.jl
Original file line number Diff line number Diff line change
Expand Up @@ -105,22 +105,32 @@ function get_titleblock(doc::WeaveDoc)
end

function strip_header(chunk::DocChunk)
if ismatch(r"^---$(?<header>.+)^---$"ms, chunk.content)
chunk.content = lstrip(replace(chunk.content, r"^---$(?<header>.+)^---$"ms, ""))
if ismatch(r"^---$(?<header>.+)^---$"ms, chunk.content[1].content)
chunk.content[1].content = lstrip(replace(chunk.content[1].content, r"^---$(?<header>.+)^---$"ms, ""))
end
return chunk
end

function format_chunk(chunk::DocChunk, formatdict, docformat)
return chunk.content
return join([format_inline(c) for c in chunk.content], "")
end

function format_inline(inline::InlineText)
return inline.content
end

function format_inline(inline::InlineCode)
isempty(inline.rich_output) || return inline.rich_output
isempty(inline.figures) || return inline.figures[end]
isempty(inline.output) || return inline.output
end

function format_chunk(chunk::DocChunk, formatdict, docformat::JMarkdown2HTML)
m = Base.Markdown.parse(chunk.content)
text = format_chunk(chunk, formatdict, nothing)
m = Base.Markdown.parse(text)
return string(Documenter.Writers.HTMLWriter.mdconvert(m))
end


#Fixes to Base latex writer
function Base.Markdown.latex(io::IO, md::Base.Markdown.Paragraph)
println(io)
Expand All @@ -144,7 +154,8 @@ end


function format_chunk(chunk::DocChunk, formatdict, docformat::JMarkdown2tex)
m = Base.Markdown.parse(chunk.content)
text = format_chunk(chunk, formatdict, nothing)
m = Base.Markdown.parse(text)
return Base.Markdown.latex(m)
end

Expand Down
54 changes: 45 additions & 9 deletions src/readers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,37 @@ pushopt(options::Dict,expr::Expr) = Base.Meta.isexpr(expr,:(=)) && (options[expr
type MarkupInput
codestart::Regex
codeend::Regex
inline::Regex
end

type ScriptInput
doc_line::Regex
doc_start::Regex
opt_line::Regex
opt_start::Regex
inline::Regex
end

type NotebookInput
inline
end

const input_formats = Dict{AbstractString, Any}(
"noweb" => MarkupInput(r"^<<(.*?)>>=\s*$",
r"^@\s*$"),
r"^@\s*$",
r"`j\s+(.*?)`"s
),
"markdown" => MarkupInput(
r"^[`~]{3,}(?:\{|\{\.|)julia(?:;|)\s*(.*?)(\}|\s*)$",
r"^[`~]{3,}\s*$"),
r"^[`~]{3,}\s*$",
r"`j\s+(.*?)`"s),
"script" => ScriptInput(
r"(^#'.*)|(^#%%.*)|(^# %%.*)",
r"(^#')|(^#%%)|(^# %%)",
r"(^#\+.*$)|(^#%%\+.*$)|(^# %%\+.*$)",
r"(^#\+)|(^#%%\+)|(^# %%\+)"),
"notebook" => NotebookInput()
r"(^#\+)|(^#%%\+)|(^# %%\+)",
r"`j\s+(.*?)`"s),
"notebook" => NotebookInput(nothing) #Don't parse inline code from notebooks
)

"""Detect the input format based on file extension"""
Expand Down Expand Up @@ -59,7 +66,7 @@ function parse_header(chunk::CodeChunk)
end

function parse_header(chunk::DocChunk)
m = match(r"^---$(?<header>.+)^---$"ms, chunk.content)
m = match(r"^---$(?<header>.+)^---$"ms, chunk.content[1].content)
if m !== nothing
header = YAML.load(string(m[:header]))
else
Expand Down Expand Up @@ -109,7 +116,7 @@ function parse_doc(document::AbstractString, format::MarkupInput)
haskey(options, :name) || (options[:name] = nothing)

if !isempty(strip(content))
chunk = DocChunk(content, docno, start_line)
chunk = DocChunk(content, docno, start_line, format.inline)
docno += 1
push!(parsed, chunk)
end
Expand Down Expand Up @@ -143,7 +150,7 @@ function parse_doc(document::AbstractString, format::MarkupInput)

#Remember the last chunk
if strip(content) != ""
chunk = DocChunk(content, docno, start_line)
chunk = DocChunk(content, docno, start_line, format.inline)
#chunk = Dict{Symbol,Any}(:type => "doc", :content => content,
# :number => docno, :start_line => start_line)
push!(parsed, chunk)
Expand Down Expand Up @@ -223,7 +230,7 @@ function parse_doc(document::AbstractString, format::ScriptInput)
elseif state == "doc" && strip(line) != "" && strip(read) != ""
state = "code"
(docno > 1) && (read = "\n" * read) # Add whitespace to doc chunk. Needed for markdown output
chunk = DocChunk(read, docno, start_line)
chunk = DocChunk(read, docno, start_line, format.inline)
push!(parsed, chunk)
options = Dict{Symbol,Any}()
start_line = lineno
Expand All @@ -244,7 +251,7 @@ function parse_doc(document::AbstractString, format::ScriptInput)
chunk = CodeChunk("\n" * strip(read), codeno, start_line, optionString, options)
push!(parsed, chunk)
else
chunk = DocChunk(read, docno, start_line)
chunk = DocChunk(read, docno, start_line, format.inline)
push!(parsed, chunk)
end

Expand Down Expand Up @@ -277,3 +284,32 @@ function parse_doc(document::String, format::NotebookInput)

return parsed
end

#Use this if regex is undefined
function parse_inline(text, noex)
return Inline[InlineText(text, 1, length(text), 1)]
end

function parse_inline(text::AbstractString, inline_ex::Regex)
ismatch(inline_ex, text) || return Inline[InlineText(text, 1, length(text), 1)]

inline_chunks = eachmatch(inline_ex, text)
s = 1
e = 1
res = Inline[]
textno = 1
codeno = 1

for ic in inline_chunks
s = ic.offset
doc = InlineText(text[e:(s-1)], e, s-1, textno)
textno += 1
push!(res, doc)
e = s + length(ic.match)
push!(res, InlineCode(ic.captures[1], s, e, codeno))
codeno += 1
end
push!(res, InlineText(text[e:end], e, length(text), textno))

return res
end