Skip to content

Commit

Permalink
Work on package documentation. Eating own tail.
Browse files Browse the repository at this point in the history
  • Loading branch information
dcjones committed Aug 16, 2013
1 parent ac3de9b commit b104e6c
Show file tree
Hide file tree
Showing 8 changed files with 512 additions and 72 deletions.
9 changes: 8 additions & 1 deletion bin/judo
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,16 @@ ap = ArgParseSettings("Generate documentation.")
arg_type = String
default = "default"
help = "template to use"
"--package"
help = "build a package's documentation"
action = :store_true
end

args = ArgParse.parse_args(ap)

Judo.collate([args["filename"]], template=args["template"], outdir=args["out"])
if args["package"]
Judo.collate(args["filename"])
else
Judo.collate([args["filename"]], template=args["template"], outdir=args["out"])
end

43 changes: 43 additions & 0 deletions doc/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
---
title: Getting Started
author: Daniel Jones
...

Judo is a Julia document generator. It takes documents written in
[pandoc markdown](http://johnmacfarlane.net/pandoc/README.html#pandocs-markdown)
and converts them into html, but differs from general purpose markdown tools in
a few ways.

1. Code blocks can be executed and their results inlined in the document,
including plots and graphics.
2. Metadata can be attached to a document in the form of YAML front-matter
(similar to Jekyll).
3. Multiple documents can be compiled and cross-linked.
4. Function and types comments can be parsed from Julia source code and
included in a document.

The end goal is to make documenting Julia code, whether it be a package, or some
quick-and-dirty analysis, as painless as possible.


# Installing

Judo can be installed like a regular Julia package, but is used somewhat
differently.

```{.julia execute="false"}
Pkg.add("Judo")
```

An executable `judo` script is now installed to
`joinpath(Pkg.dir("Judo"), "bin")` (typically `~/.julia/Judo/bin/judo`), which
you may want to add to your `PATH` variable.


# Using

`judo -h` will give you some idea of how Judo is invoked.




85 changes: 85 additions & 0 deletions doc/markdown.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
---
title: Judo Markdown
author: Daniel Jones
...

Markdown interpreted by Judo is
[pandoc markdown](http://johnmacfarlane.net/pandoc/README.html#pandocs-markdown)
with some extensions.

# YAML Front-Matter

Markdown is great, but its often useful to be able to attach metadata to a
document. Judo borrows an idea from [Jekyll](http://jekyllrb.com/) to overcome
this. Before parsing a markdown document, Judo will look for a
leading [YAML](http://yaml.org/) document.

Front-matter documents can be delineated with with `---`, indicating the
beginning of a YAML document, and `...`, indicating the end. This is standard
YAML, so document metadata can be parsed with any YAML parser.

There is no restrictions on what is included in the metadata, but a few fields
are used directly by Judo.


## Metadata Fields

`author`, `authors`: gives the name of the document's author or authors.

`title`: gives the title of the document, which is used when generating a table
of contents.

`data`: the date when the document was last modified.

TODO: In the future more fields will be added, in particular `topics` to ease
the generation of an index.


# Executable Code Blocks

Regular markdown code blocks are by default interpreted as Julia code and
executed.

For example this code block will display its output inline in the compiled
version of this document.

```julia
println("Hello world!")
```

## Controlling Execution

Pandoc markdown allows attaching
[attributes](http://johnmacfarlane.net/pandoc/README.html#header-identifiers-in-html-latex-and-context)
to code blocks. These are used in Judo to control how code blocks are handled.

The following key/value pairs are supported.

* `execute="false"`: Do not execute the code block.
* `hide="true"`: Do not display the code block itself.
* `display="false"`: Do not display the output from executing the code block.
* `results="block"`: Display the result of the last expression in the block.
* `results="expression"`: Display the result of every expression in the block.

Note: pandoc is pretty picky about how these are parsed. For example, quotation
marks around the value in key/value pairs are mandatory.


## Non-Julia Executables

Not just Julia code can be executed. Code blocks can also be used to include
output from latex, graphiviz, or directly include svg code. Others can be added
easily.


```graphviz
digraph {
"write documentation" -> "compile documentation";
"compile documentation" -> "enjoy an overwhelming sense of satisfaction";
}
```





121 changes: 91 additions & 30 deletions src/Judo.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@

module Judo

import Base: start, next, done, display
import Base: start, next, done, display, writemime
import JSON
import YAML
import Mustache


include("collate.jl")
Expand Down Expand Up @@ -116,18 +117,38 @@ end


# Display functions that render output and insert them into the document.
function display(doc::WeaveDoc, ::@MIME("text/plain"), data)

const supported_mime_types =
{ MIME("text/html"),
MIME("image/svg+xml"),
MIME("image/png"),
MIME("text/latex"),
MIME("text/vnd.graphviz"),
MIME("text/plain") }


function display(doc::WeaveDoc, data)
for m in supported_mime_types
if mimewritable(m, typeof(data))
display(doc, m, data)
break
end
end
end


function display(doc::WeaveDoc, m::@MIME("text/plain"), data)
block =
{"CodeBlock" =>
{{"", {"output"}, {}}, data}}
{{"", {"output"}, {}}, stringmime(m, data)}}
push!(doc.display_blocks, block)
end


function display(doc::WeaveDoc, ::@MIME("text/latex"), data)
function display(doc::WeaveDoc, m::@MIME("text/latex"), data)
# latex to dvi
input_path, input = mktemp()
print(input, data)
writemime(input, m, data)
flush(input)
seek(input, 0)
latexout_dir = mktempdir()
Expand All @@ -139,14 +160,14 @@ function display(doc::WeaveDoc, ::@MIME("text/latex"), data)
output = readall(`dvisvgm --stdout --no-fonts $(latexout_path)` .> SpawnNullStream())
run(`rm -rf $(latexout_dir)`)

display("image/svg+xml", output)
display(doc, MIME("image/svg+xml"), output)
end


function display(doc::WeaveDoc, ::@MIME("image/svg+xml"), data)
function display(doc::WeaveDoc, m::@MIME("image/svg+xml"), data)
filename = @sprintf("%s_figure_%d.svg", doc.name, doc.fignum)
out = open(filename, "w")
write(out, data)
out = open(joinpath(doc.outdir, filename), "w")
writemime(out, m, data)
close(out)

alttext = @sprintf("Figure %d", doc.fignum)
Expand Down Expand Up @@ -179,14 +200,49 @@ function display(doc::WeaveDoc, ::@MIME("image/svg+xml"), data)
end


function display(doc::WeaveDoc, ::@MIME("text/vnd.graphviz"), data)
function display(doc::WeaveDoc, m::@MIME("image/png"), data)
filename = @sprintf("%s_figure_%d.png", doc.name, doc.fignum)
out = open(joinpath(doc.outdir, filename), "w")
writemime(out, m, data)
close(out)

alttext = @sprintf("Figure %d", doc.fignum)
figurl = filename
caption = ""

block =
{"Para" =>
{{"Image" =>
{{{"Str" => alttext}},
{figurl, ""}}}}}

doc.fignum += 1
push!(doc.display_blocks, block)
end


function display(doc::WeaveDoc, m::@MIME("text/vnd.graphviz"), data)
output, input, proc = readandwrite(`dot -Tsvg`)
write(input, data)
writemime(input, m, data)
close(input)
display("image/svg+xml", readall(output))
display(doc, MIME("image/svg+xml"), readall(output))
end


function display(doc::WeaveDoc, m::@MIME("text/html"), data)
block = {"RawBlock" =>
{"html", stringmime(m, data)}}
push!(doc.display_blocks, block)
end


# This is maybe an abuse. TODO: This is going to be a problem.
writemime(io, m::@MIME("text/vnd.graphviz"), data::String) = write(io, data)
writemime(io, m::@MIME("image/vnd.graphviz"), data::String) = write(io, data)
writemime(io, m::@MIME("image/svg+xml"), data::String) = write(io, data)
writemime(io, m::@MIME("text/latex"), data::String) = write(io, data)


# Transform a annotated markdown file into a variety of formats.
#
# This reads markdown input, optionally prefixed with a YAML metadata document,
Expand All @@ -209,7 +265,8 @@ end
#
function weave(input::IO, output::IO;
outfmt=:html5, name="judo", template=nothing,
toc=false, outdir=".")
toc::Bool=false, outdir::String=".", dryrun::Bool=false,
keyvals::Dict=Dict())
input_text = readall(input)

# parse yaml front matter
Expand All @@ -221,7 +278,8 @@ function weave(input::IO, output::IO;
end

# first pandoc pass
pandoc_metadata, document = JSON.parse(pandoc(input_text, :markdown, :json))
pandoc_metadata, document =
JSON.parse(pandoc(input_text, :markdown, :json))
prev_stdout = STDOUT
stdout_read, stdout_write = redirect_stdout()
doc = WeaveDoc(name, outfmt, stdout_read, stdout_write, outdir)
Expand All @@ -236,19 +294,27 @@ function weave(input::IO, output::IO;
for subblock in nameblocks
if subblock == "Space"
write(name, " ")
elseif is(subblock, Dict)
elseif isa(subblock, Dict)
write(name, subblock["Str"])
end
end
push!(sections, (level, takebuf_string(name)))
push!(doc.blocks, process_block(block))
if !dryrun
push!(doc.blocks, process_block(block))
end
elseif dryrun
continue
elseif isa(block, Dict) && haskey(block, "CodeBlock")
process_code_block(doc, block)
else
push!(doc.blocks, process_block(block))
end
end

if dryrun
return metadata, sections
end

# splice in metadata fields that pandoc supports
pandoc_metadata_keys = ["title" => "docTitle",
"authors" => "docAuthors",
Expand Down Expand Up @@ -284,6 +350,10 @@ function weave(input::IO, output::IO;
JSON.print(buf, {pandoc_metadata, doc.blocks})

args = {}
for (k, v) in keyvals
push!(args, "--variable=$(k):$(v)")
end

if template != nothing
push!(args, "--template=$(template)")
end
Expand Down Expand Up @@ -428,37 +498,28 @@ function process_code_block(doc::WeaveDoc, block::Dict)
end
empty!(doc.display_blocks)
else
if !hide
if !keyvals["hide"]
push!(doc.blocks, block)
end

class = classes[1]
if display
if keyvals["display"]
if class == "graphviz"
display("text/vnd.graphviz", text)
elseif class == "latex"
display("text/latex", text)
elseif class == "svg"
display("image/svg+xml", text)
end
append!(doc.blocks, doc.display_blocks)
end
end
end


# Evaluate an expression and return its results. Catch any errors and return
# them in string form.
# Evaluate an expression and return its result and a string.
function safeeval(ex::Expr)
try
string(eval(WeaveSandbox, ex))
catch e
errout = IOBuffer()
print(errout, "ERROR: ")
Base.error_show(errout, e)
result = bytestring(errout)
close(errout)
result
end
string(eval(WeaveSandbox, ex))
end


Expand Down
Loading

0 comments on commit b104e6c

Please sign in to comment.