Information for Package Developers

Custom Inline Display

Juno's inline display system operates on three distinct levels:

  1. If you create a new type and don't define a show method for it, Juno will use a fallback that lazily shows all fields.
  2. If you've defined a show method for the application/prs.juno.inline MIME type then Juno will
  • display what is printed by that method or
  • display the above fallback for the object returned by the show method.
  1. If you've defined a show method for the text/plain MIME type Juno will use that to create a simple Tree View.
  2. TreeViews.jl allows customizing everything about how your type is displayed.

The following will show how to use the TreeViews.jl API for pretty-printing in a few simple cases.

First, let's define some custom types we want to display:

struct Foo
    field1
    field2
end

struct Bar
    a
    field1
end

By default, Juno displays those like this:

custom rendering 1

This default rendering method will not be used if you define e.g.

Base.show(io::IO, ::MIME"text/plain", ::Foo) = print(io, "Foo")

for displaying this nicely in the REPL. If you also define a Base.show(io::IO, ::MIME"application/prs.juno.inline", ::Foo) method, that will be used instead. As a special case, you can also return an object from that show method, and Juno will show that with its default display methods (which allows you to recover what is shown above):

Base.show(io::IO, ::MIME"application/prs.juno.inline", x::Foo) = x

To switch to a TreeViews.jl display we can simply overload the following methods for our types:

import TreeViews: hastreeview, numberofnodes, treelabel, treenode
hastreeview(::Foo) = true
hastreeview(::Bar) = true

Since TreeViews.jl implements sensible defaults for custom types, this gives us

custom rendering 2

It is of course possible to customize this to your heart's content. For example, we might only want to display Foos first field with

numberofnodes(::Foo) = 1

or change the Foo in the first line to something else:

treelabel(io::IO, x::Foo, ::MIME"text/plain") = print(io, "I'm a Foo.")

custom rendering 3

Juno accepts a few different MIME types:

  • text/plain: Probably best compatibility.
  • text/html: Allows much richer display options (e.g. LaTeX).
  • application/prs.juno.inline: Same as text/html, but specific to Juno.

Even text/plain allows for some (limited) control over styling (colors, decorations). In general, you can print the correct SGR codes and everything will work, but I'd recommend using Crayons.jl or a similar library instead:

# plain SGR
treelabel(io::IO, x::Foo, ::MIME"text/plain") =
  print(io, "\x1b[32mHello\x1b[0m there")

using Crayons
treelabel(io::IO, x::Foo, i::Int, ::MIME"text/plain") =
  print(io, crayon"(250,20,105) bold"("General"), " Kenobi")

custom rendering 4

Note

Those styles also apply to display in the REPL, provided for example by the as-of-yet unregistered REPLTreeViews.jl package:

repl tree views

Using text/html allows for greater customization by using inline CSS

treelabel(io::IO, x::Foo, i::Int, ::MIME"text/html") =
  print(io, "<span><span style=\"color:aqua; font-size:0.7em\">General</span> Kenobi</span>")

custom rendering 5

The application/prs.juno.inline MIME type allows you to make use of the styling Atom uses:

treelabel(io::IO, x::Foo, ::MIME"application/prs.juno.inline") =
  print(io, "<span class=\"syntax--support syntax--type syntax--julia\">Junoooooooo!</span>")

custom rendering 6

It's also possible to e.g. print no label for Foos first field and handle everything in with treenode:

treelabel(io::IO, x::Foo, i::Int, ::MIME"application/prs.juno.inline") = print(io, "")
using Markdown
treenode(x::Foo, i::Int) = MD("""
    ## Markdown

    with ``\\LaTeX``\n
    and `code = 2+sqrt(4)`
    """)

custom rendering 7

To hide the treenode display, simply return missing:

treenode(x::Foo, i::Int) = missing
treelabel(io::IO, x::Foo, i::Int, ::MIME"application/prs.juno.inline") = print(io, "...")

custom rendering 8

Nothing at all will be shown if treelabel doesn't print anything and treenode returns missing.

Displaying Plots and Graphics

Plots can be displayed by providing a show method for one of the following MIME types (ordered by priority):

  1. application/prs.juno.plotpane+html - rendered in a webview
  2. image/svg+xml - rendered in a webview
  3. image/png
  4. image/jpeg
  5. image/tiff
  6. image/bmp
  7. image/gif

The first two of those MIME types are rendered in a webview to a) prevent XSS and b) make sure not to crash Atom for big or complex graphics. For the others we provide some basic pan and zoom utilities.

So if you want to e.g. display an svg you can do the following:

struct Baz
    data
end

Base.show(io::IO, ::MIME"image/svg+xml", b::Baz) = print(io, b.data)
Baz(open(f -> read(f, String), "emu.svg"))

plot pane svg

application/prs.juno.plotpane+html is HTML, but also indicates that you want your type to be displayed in Juno's Plot Pane.

struct Blah
    data
end

function Base.show(io::IO, ::MIME"application/prs.juno.plotpane+html", b::Blah)
    colors = get(io, :juno_colors, nothing)
    size = get(io, :juno_plotsize, [100, 100])

    html = """
    <div style="
        background-color: #eee;
        color: #222;
        width: $(size[1]-40)px;
        height: $(size[2]-40)px;
        position: absolute;
        top: 0;
        left: 0;
        padding: 20px;
        margin: 0;
        ">
        <span>$(b.data)</span>
        <br/>
        <input/>
    </div>
    """

    print(io, html)
end

Blah("Input stuff here:")

plot pane html