You are on page 1of 101

Awesome Asciidoctor Notebook

Experience Asciidoctor with code snippets


Hubert A. Klein Ikkink (mrhaki)
This book is for sale at http://leanpub.com/awesomeasciidoctornotebook
This version was published on 2016-11-11

This is a Leanpub book. Leanpub empowers authors and publishers with the Lean
Publishing process. Lean Publishing is the act of publishing an in-progress ebook using
lightweight tools and many iterations to get reader feedback, pivot until you have the
right book and build traction once you do.
2015 - 2016 Hubert A. Klein Ikkink (mrhaki)

Tweet This Book!


Please help Hubert A. Klein Ikkink (mrhaki) by spreading the word about this book on
Twitter!
The suggested tweet for this book is:
I just bought Awesome Asciidoctor Notebook with Asciidoctor blog posts bundled into
one book. #asciidoctor @mrhaki
The suggested hashtag for this book is #awesomeasciidoctornotebook.
Find out what other people are saying about the book by clicking on this link to search
for this hashtag on Twitter:
https://twitter.com/search?q=#awesomeasciidoctornotebook

Also By Hubert A. Klein Ikkink (mrhaki)


Groovy Goodness Notebook
Grails Goodness Notebook
Gradle Goodness Notebook
Spocklight Notebook
Ratpacked Notebook

This book is dedicated to my lovely family. I love you.

Contents
About Me . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

ii

Tables . . . . . . . . . . . . . . . . . . . . . . .
Changing Table and Column Width . . .
Table Column and Cell Alignment . . . .
Changing the Grid and Frame of Tables .
Span Cell over Rows and Columns . . . .
Repeating Cell Contents . . . . . . . . . .
Using Asciidoc in Tables . . . . . . . . . .
Styling Columns and Cells in Tables . . .
Escaping Pipe Symbol in Tables . . . . .
CSV and DSV Tables . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

1
1
3
5
8
10
11
13
16
17

Source code . . . . . . . . . . . . . . . . . . . . . . . .
Change Source Code Indentation . . . . . . . .
Adding Line Numbers to Source Code Listings
Include Partial Parts from Code Samples . . . .
Include Only Certain Lines from Included Files
Explain Code with Callouts . . . . . . . . . . . .
Highlight Lines In Source Code Listings . . . .
Changing Highlight.js Theme . . . . . . . . . . .
Source Syntax Highlighting With Prism . . . . .

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

20
20
21
23
24
25
26
28
30

Extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Use Asciidoctor Diagram with Gradle . . . . . . . . . . .
Write Extensions Using Groovy (or Java) . . . . . . . . .
Use Inline Extension DSL with Gradle . . . . . . . . . . .
Using Ruby Extensions With Asciidoctor Gradle Plugin

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

34
34
36
39
41

Miscellaneous . . . . . . . . . . . . . . . . . . . . .
Using Comments . . . . . . . . . . . . . . . . .
Which Asciidoctor Version is Used? . . . . . .
Disable Last Updated Text in Footer . . . . . .
Adding Custom Content to Head and Footer .
Include Raw HTML . . . . . . . . . . . . . . . .
Use Inline Icons . . . . . . . . . . . . . . . . . .
Changing the FontAwesome CSS Location . .
Change URI Scheme for Assets . . . . . . . . .
Replacements For Text To Symbols . . . . . .

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

43
43
44
44
45
47
48
50
51
52

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

CONTENTS

Turn Section Titles Into Links . . . . . . . . . . . . . . . . .


Leave Section Titles Out of Table Of Contents . . . . . . . .
Use Captions For Listing Blocks . . . . . . . . . . . . . . . .
Change Level Offset For Included Documents . . . . . . . .
Use Counters in Markup . . . . . . . . . . . . . . . . . . . . .
Links Without URI Scheme . . . . . . . . . . . . . . . . . . .
Use Link Attributes . . . . . . . . . . . . . . . . . . . . . . . .
Customize How Missing Attributes Are Handled . . . . . .
Substitute Attribute in Listing Block . . . . . . . . . . . . . .
Escape Attribute References . . . . . . . . . . . . . . . . . .
Using Conditional Directives . . . . . . . . . . . . . . . . . .
Conditional Directive to Check If Document is On GitHub
Using Document Fragments . . . . . . . . . . . . . . . . . . .
Keep Line Breaks in Paragraphs . . . . . . . . . . . . . . . .
Apply Custom Styling to Blocks . . . . . . . . . . . . . . . . .
Nested Delimited Blocks . . . . . . . . . . . . . . . . . . . . .
Do Not Wrap Lines in Listing or Literal Blocks . . . . . . . .
Creating a Checklist . . . . . . . . . . . . . . . . . . . . . . .
Change Start Number for Numbered List . . . . . . . . . . .
Customize the Figure Captions . . . . . . . . . . . . . . . . .
Trick To Use Caption Labels And Numbers In References .
Changing Values for Default Captions . . . . . . . . . . . . .
Display Keyboard Shortcuts . . . . . . . . . . . . . . . . . . .
Using Asciidoctor In Javadoc Comments . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

52
53
55
57
59
60
61
62
63
64
65
68
69
70
71
73
76
77
78
79
81
84
86
87

About Me
I am born in 1973 and live in Tilburg, the Netherlands, with my beautiful wife and three
gorgeous children. I am also known as mrhaki, which is simply the initials of his name
prepended by mr. The following Groovy snippets shows how the alias comes together:
['Hubert', 'Alexander', 'Klein', 'Ikkink'].inject('mr') { alias, name ->
alias += name[0].toLowerCase()
}

(How cool is Groovy that we can express this in a simple code sample ;-) )
I studied Information Systems and Management at the Tilburg University. After finishing
my studies I started to work at a company which specialized in knowledge-based software.
There I started writing my first Java software (yes, an applet!) in 1996. Over the years my
focus switched from applets, to servlets, to Java Enterprise Edition applications, to Springbased software.
In 2008 I wanted to have fun again when writing software. The larger projects I was working
on were more about writing configuration XML files, tuning performance and less about
real development. So I started to look around and noticed Groovy as a good language to
learn about. I could still use existing Java code, libraries, and my Groovy classes in Java.
The learning curve isnt steep and to support my learning phase I wrote down interesting
Groovy facts in my blog with the title Groovy Goodness. I post small articles with a lot of
code samples to understand how to use Groovy. Since November 2011 I am also a DZone
Most Valuable Blogger (MVB); DZone also posts my blog items on their site.
In 2010, 2011, 2012, 2013 and 2014 I was invited to speak at Gr8Conf in Copenhagen,
Denmark. This is a very good conference with all the project leaders of Groovy and Groovyrelated projects. In 2014 I also spoke at the Gr8Conf US. In 2015 I got a change to speak at
the Greach conference in Madrid. A very nice conference with all Groovy related subjects.
In November 2010 I presented a Gradle talk at the J-Fall conference of the Dutch Java User
Group. In November 2011 I presented about the new features in Groovy 1.8 at the same
conference. During the 2013 edition I presented a hands-on Gradle workshop and in 2014
I gave a talk about AsciiDoctor. The conference is visited by 1000 Java developers and I got
the chance to educate some of them about the greatness of Gradle and Groovy.
I work for a company called JDriven in the Netherlands. JDriven focuses on technologies
that simplify and improve development of enterprise applications. Employees of JDriven
have years of experience with Java and related technologies and are all eager to learn about
new technologies. I work on projects using Grails and Java combined with Groovy and
Gradle.

Introduction
When I started to learn about Asciidoctor I wrote done little code snippets with features
of Asciidoctor I found interesting. To access my notes from different locations I wrote the
snippets with a short explanation in a blog: Messages from mrhaki. I labeled the post as
Awesome Asciidoctor, because I thought this is awesome stuff, and that is how the Awesome
Asciidoctor series began.
A while ago I bundled all my blog Groovy Goodness blog posts in a book published at
Leanpub. Leanpub is very easy to use and I could use Markdown to write the content,
which I really liked as a developer. So it felt natural to also bundle the Grails Goodness
blog posts and Gradle Goodness blog posts at Leanpub.
In this book the blog posts are bundled and categorized into sections. Within each section
blog posts that cover similar features are grouped. The book is intended to browse through
the subjects. You should be able to just open the book at a random page and learn more
about Asciidoctor. Maybe pick it up once in a while and learn a bit more about known and
lesser known features of Asciidoctor.
I hope you will enjoy reading the book and it will help you with learning about Asciidoctor,
so you can apply all the awesomeness in your projects.

Code samples
Code samples in the book can contain a backslash (\) at the end of the line to
denote the line actually continues. This character is not part of the code, but for
better formatting of the code blocks in the book the line is split. If you want to
use the code you must keep this in mind.

Tables
Changing Table and Column Width
When we define a table in Asciidoctor the columns all have the same width and the table
will the whole width of the page. Of course we can change this when we define the table.
We can change the table width with the width attribute. We specify the column width with
the cols attribute.
First we will change the width of the columns. We can specify proportional integer values
or a percentage to specify the width of a column. In the following sample Asciidoctor file
we use proportional values in the first table. The first column has value 1 and the second
column value 3. This means the second column should be 3 times as wide as the first
column. In the second table the column width is defined with percentage values. The first
column should occupy 60% of the table width and the last column the rest.
// Define table with proportional column width.
.Table with relative column widths (1,3)
[cols="1,3"]
|===
| Name | Description
| Asciidoctor
| Awesome way to write documentation
|===

// Define table with column width as percentage values.


.Table with percentage column widths (60%,40%)
[cols="60%,40%"]
|===
| Name | Description
| Asciidoctor
| Awesome way to write documentation
|===

When we transform the Asciidoctor source to HTML we get the following result:

Tables

We see in the examples that the width of the table is the same as the width of the page. To
make a smaller table we set the width attribute for a table. The value must be a percentage.
Lets create a sample Asciidoctor file and define a table with a width of 50%:
// Define table with default width for comparison.
.Table full width (default)
|===
| Name | Description
| Asciidoctor
| Awesome way to write documentation
|===

// Define new table and set width to 50%.


.Table half width (50%)
[width="50%"]
|===
| Name | Description
| Asciidoctor
| Awesome way to write documentation
|===

And if we look at the generated HTML for example we can see the second table is half the
width of the first:

Tables

Written with Asciidoctor 1.5.1.


Original blog post written on November 07, 2014.

Table Column and Cell Alignment


Creating a table with Asciidoctor is a breeze. The syntax is very clear and the HTML output
shows a very nice looking table. But there is also a lot of configuration we can do when we
define a table. For example by default all columns are left aligned, but we can change this
to have values centered or right aligned in columns. We can even set the vertical alignment
for columns. And if this is not enough we can change the horizontal and vertical alignment
per cell.
Lets start with a simple table with three columns. We want the first column to be centered,
the middle column to be left aligned and the last column should be right aligned. To achieve
this we must configure the cols attribute for our table definition. We use the following
symbols to define the alignment:
<: left align values (default)
>: right align values
: center values
We create a new Asciidoctor file with the following contents:
// File: table.adoc
//
//
//
//

We define a table with 3-columns.


First column is centered,
second column is left aligned and
third column is right aligned.

[cols="^,<,>", options="header"]
|===
| Name
| Description
| Version

Tables

| Asciidoctor
| Awesome way to write documentation
| 1.5.0
|===

When we create HTML output from this source we get the following output:

We have now defined the horizontal alignment. To include vertical alignment we must
add dot (.) to the horizontal alignment value and then the vertical alignment value. The
following vertical alignment values can be used:
<: top align values (default)
>: bottom align values
: center values
In the following sample Asciidoctor file we add vertical alignment configuration to our
previous table:
// File: table.adoc
//
//
//
//

We define a table with 3-columns.


First column is centered and bottom aligned,
second column is left and top aligned and
third column is right aligned and centered vertically.

[cols="^.>,<.<,>.^", options="header"]
|===
| Name
| Description
| Version
| Asciidoctor
| Awesome way to write documentation
| 1.5.0
|===

We get the following HTML table when we process that source file:

Tables

Finally we can alter the horizontal and vertical alignment per cell. We use the alignment
configuration symbols before the pipe symbol (|) of a cell definition. This overrules any
alignment configuration set in the cols definition. In the next Asciidoctor file we combine
all these settings for a table:
// File: table.adoc
//
//
//
//
//
//

We define a table with 3-columns.


The row header has all cell values
centered.
The first table row cell is right aligned.
The last table row cell is horizontal
and vertical centered.

[cols="3*", options="header"]
|===
^| Name
^| Description
^| Version
>| Asciidoctor
| Awesome way to write documentation
^.^| 1.5.0
|===

And when we look at the output we see all alignment configuration applied to our table:

Written with Asciidoctor 1.5.0.


Original blog post written on November 06, 2014.

Changing the Grid and Frame of Tables


We can change the frames and grid of tables we define in Asciidoctor. We use the frames
attribute to change the outside frame of a table. We can choose between topbot for top
and bottom, sides for only a frame at the sides of the table, none if we dont want a frame.
The default value all create a frame around our table with top, sides and bottom.

Tables

To change the inner grid of a table we use the grids table attribute. The default value all
displays a grid for columns and rows inside the table. The value cols only displays a grid
between columns, value rows display a grid between rows and with value none there will
be no grid inside our table.
The following Asciidoc sample shows the definition of four tables with different values for
the cols table attribute:
.Table with top and bottom frame (topbot)
[frame="topbot"]
|===
| Name | Description
| Asciidoctor
| Awesome way to write documentation
|===

.Table with no frame (none)


[frame="none"]
|===
| Name | Description
| Asciidoctor
| Awesome way to write documentation
|===

.Table with only sides frame (sides)


[frame="sides"]
|===
| Name | Description
| Asciidoctor
| Awesome way to write documentation
|===

.Table with default frame (all)


[frame="all"]
|===
| Name | Description
| Asciidoctor
| Awesome way to write documentation
|===

When we generate output using the HTML backend we get the following result:

Tables

In the next sample we have four tables with different values for the cols attribute:
.Table with no grid (none)
[grid="none", frame="none"]
|===
| Name | Description
| Asciidoctor
| Awesome way to write documentation
|===
.Table with only columns grid (cols)
[grid="cols", frame="none"]
|===
| Name | Description
| Asciidoctor
| Awesome way to write documentation
|===
.Table with only rows grid (rows)
[grid="rows", frame="none"]
|===
| Name | Description
| Asciidoctor
| Awesome way to write documentation
|===
.Table with default rows and columns grid (all)
[grid="all", frame="none"]

Tables

|===
| Name | Description
| Asciidoctor
| Awesome way to write documentation
|===

And we get the following output when we transform this source to HTML:

Written with Asciidoctor 1.5.1.


Original blog post written on November 20, 2014.

Span Cell over Rows and Columns


When we define a table in Asciidoctor we might want to span a cell over multiple columns
or rows, instead of just a single column or row. We can do this using a cell specifier with the
following format: column-span.row-span+. The values for column-span and row-span define
the number of columns and rows the cell must span. We put the cell specifier before the
pipe symbol (|) in our table definition.
In the following example Asciidoctor markup we have three tables. In the first table we
span a cell over 2 columns, the second table spans a cell over 2 rows and in the final table
we span a cell over both 2 columns and rows.

Tables

== Table cell span


.Cell spans columns
|===
| Name | Description
| Asciidoctor
| Awesome way to write documentation
// This cell spans 2 columns, indicated
// by the number before the + sign.
// The + sign
// tells Asciidoctor to span this
// cell over multiple columns.
2+| The statements above say it all
|===

.Cell spans rows


|===
| Name | Description
// This cell spans 2 rows,
// because the number after
// the dot (.) specifies the number
// of rows to span. The + sign
// tells Asciidoctor to span this
// cell over multiple rows.
.2+| Asciidoctor
| Awesome way to write documentation
| Works on the JVM
|===

.Cell spans both rows and columns


|===
| Col1 | Col2 | Col 3
// We can combine the numbers for
// row and column span within one
// cell specifier.
// The number before the dot (.)
// is the number of columns to span,
// the number after the dot (.)
// is the number of rows to span.
2.2+| Cell spans 2 cols, 2 rows
| Row 1, Col 3
| Row 2, Col 3
|===

Tables

10

If we transform our source to HTML we get the following tables:

Written with Asciidoctor 1.5.1.


Original blog post written on December 04, 2014.

Repeating Cell Contents


With Asciidoctor we can repeat cell contents if we prefix the cell separator pipe symbol (|)
with the number of times we want to repeat the cell followed by an asterisk (*).
In the following example Asciidoctor source file we define two tables and add 2* to cells
that we want to repeat two times:
// 3-column table, where the first column
// cell value is not repeated, and the
// cell value for columns 2 and 3 is
// repeated.
|===
| Column | Value | Value
| Name
2*| Asciidoctor
| Description
2*| Awesome way to write documentation

Tables

11

|===

// One column table. So the repeated


// cells are each on their own row.
|===
| Column
2*| Asciidoctor
2*| Awesome way to write documentation
|===

When we generate a HTML document from this source we see the following result:

Written with Asciidoctor 1.5.1.


Original blog post written on December 02, 2014.

Using Asciidoc in Tables


When we define a table in Asciidoctor and want to use Asciidoc in a table cell it is not
interpreted as Asciidoc by default. The text is literally shown and this might not be what
we expect. But we can force Asciidoctor to interpret the cell contents as Asciidoctor.
Lets start with a very simple table. The last cell of the first row contains some Asciidoc
markup:

Tables

12

:icons: font
// Simple table where we apply some
// Asciidoc markup in the cell contents.
|===
| Name | Description
| Asciidoctor
| NOTE: *Awesome* way to write documentation
|===

When we transform this Asciidoctor source to HTML we see the following output:

Notice that we dont get a nice image for our NOTE is not shown as image when it used in a
table cell.
To change this behavior we can configure the table. We can configure a column to have
Asciidoc content that needs to be interpreted or we can configure at cell level we want the
contents to be interpreted as Asciidoc. We use the character a in the cols attribute when
we define the table. Or we use the character a before the table cell separator (|). In the
next sample Asciidoctor file we use both ways to make sure the cell contents is Asciidoc
markup that needs to be transformed as well:
:icons: font
// We use the cols attribute for our table
// and specify that the contents of the second
// column is Asciidoc markup.
[cols=",a"]
|===
| Name | Description
| Asciidoctor
| NOTE: *Awesome* way to write documentation
|===

// Or we configure per cell the contents


// is Asciidoc markup.
|===
| Name | Description
| Asciidoctor

13

Tables

//
//
//
a|

We specify for this specific cell the


contents is Asciidoc that needs to
be processed.
NOTE: *Awesome* way to write documentation

|===

Once we have defined the table we get the following generated HTML:

And this time the cell contents is transformed as well.


Written with Asciidoctor 1.5.1.
Original blog post written on November 10, 2014.

Styling Columns and Cells in Tables


In a previous post we learned how to use Asciidoc markup in a table. The a character is
just one of many styles we can define in our table. In this blog post we see which style
options we have. We can either use the cols attribute to define a style for a whole column
or specify per cell the style.
We can use the following styles:

e: emphasized
a: Asciidoc markup
m: monospace
h: header style, all column values are styled as header
s: strong
l: literal, text is shown in monospace font and line breaks are kept
d: default
v: verse, keeps line breaks

The following Asciidoctor source uses the different styles as cols attribute values:

Tables

14

.Table with column style e,a,m


[cols="e,a,m"]
|===
| Emphasized (e) | Asciidoc (a) | Monospaced (m)
| Asciidoctor
| NOTE: *Awesome* way to write documentation
| It is just code
|===
.Table with column style h,s,l
[cols="h,s,l"]
|===
| Header (h) | Strong (s) | Literal (l)
| Asciidoctor
| Awesome way to write documentation
| It is
just code
|===

.Table with column style d,v


[cols="d,v"]
|===
| Default (d) | Verse (v)
| Asciidoctor
| Awesome way
to write
documentation
|===

When we transform this into HTML using the Asciidoctor HTML backend we get the
following result:

Tables

15

We can also override a column styling per cell. We must put the correct styling character
before the pipe symbol (|), so the contents of the cell is styled differently:
Table with row style e,a,m in second row
|===
| Emphasized | Asciidoc | Monospaced
| Asciidoctor
| NOTE: *Awesome* way to write documentation
| It is just code
e| Asciidoctor
a| NOTE: *Awesome* way to write documentation
m| It is just code
|===

And the following HTML is generated when we process this Asciidoctor source:

Tables

16

Written with Asciidoctor 1.5.1.


Original blog post written on November 10, 2014.

Escaping Pipe Symbol in Tables


To define a table in Asciidoc is easy. Table cells are separated basically by pipe symbols (|).
But if we want to use a pipe-symbol as cell content we need to escape the pipe-symbol
with a backslash (\)
The following Asciidoc code is transformed to a correct HTML table output:
.Sample table with pipe-symbol in cell content
|===
| Operator | Method
| a + b
| a.plus(b)
| a - b
| a.minus(b)
| a \| b
| a.or(b)
|===

The generated HTML table looks like this for example:

Tables

17

Generated with Asciidoctor 0.1.4.


Original blog post written on June 04, 2014.

CSV and DSV Tables


With Asciidoctor we can create tables where the header and rows are in CSV (Comma
Separated Values) and DSV (Delimiter Separated Values) format. Normally we use a pipesymbol (|) to separate cell values. This is actually PSV (Prefix Separated Values) :-).
In the following Asciidoctor markup we create a very simple table with a header and two
rows using CSV:
= Tables
== CSV table
[format="csv", options="header"]
|===
Writing tools, Awesomeness
Asciidoctor, Oh yeah!
MS Word, No!
|===

We generate this into HTML and we get the following result:

Asciidoctor provides also another way to define the above table:

Tables

18

= Tables
== CSV table
//
//
//
//
//

Define table using CSV syntax.


The start and end of the table is defined
as ,=== instead of |===.
Also the header row is followed by new line,
to indicate it is the header row.

,===
Writing tools, Awesomeness
Asciidoctor, Oh yeah!
MS Word, No!
,===

// We can also specify a separator.


[format="csv", separator=";", options="header"]
|===
Name;Description
Asciidoctor;Awesome way to write documentation
|===

The previous samples used a comma to separate values, but we can also use colon (:). The
next sample contains tables defined with DSV:
== DSV table
[format="dsv", options="header"]
|===
Writing tools:Awesomeness
Asciidoctor:Oh yeah!
MS Word:No!
|===
// Alternative syntax:
:===
Writing tools: Awesomeness
Asciidoctor: Oh yeah!
MS Word: No!
:===

With the include directive we can also include data from an external CSV of DSV file to
create a table (of course also the traditional pipe-symbol separated format can be in an
external file):

Tables

= Table with external data


[format="csv", options="header"]
|===
include::tools.csv[]
|===

The file tools.cv has the following contents:


Writing tools, Awesomeness
Asciidoctor, Oh yeah!
MS Word, No!

Code written with Asciidoctor 1.5.0.


Original blog post written on November 05, 2014.

19

Source code
Change Source Code Indentation
Writing documentation and including source files is made very easy in Asciidoc and
Asciidoctor. We can use the include macro to include a source file in our documentation.
This way we can write source code, test the code (preferable automated) and the code in
our documentation always works and will be up to date. We can use the indent attribute
when we include source code to change the indentation. Tab characters are replaced with
4 spaces when we use the indent attribute. We can specify the number of spaces the code
needs to be indented with a value of 0 or greater.
The following sample Asciidoc markup will remove all indentation of the original code
block which is indented with 4 spaces, because we use the value 0:
[source,groovy,indent=0]
---def sample = (1..4).collect { it * 2 }
assert sample == [2,4,6,8]
----

If we generate HTML we get the following result:

The code still looks indented, but that is done via CSS styling. The padding is set to a value
greater than 0, but we can change that of course with our own CSS stylesheet.
In the following sample we set the indent attribute with the value 8:
[source,groovy,indent=8]
---def sample = (1..4).collect { it * 2 }
assert sample == [2,4,6,8]
----

We get the following result:

In the final sample we use the include macro to include source code. We can still use the
indent attribute like we did with the inline code samples:

Source code

21

[source,groovy,indent=3]
include::indent.groovy[]

Here is the resulting HTML:

Code written with Asciidoctor 0.1.4.


Original blog post written on June 10, 2014.

Adding Line Numbers to Source Code Listings


When we write technical documentation with Asciidoctor we can easily include source
code listings. When we use the coderay or pygments source code highlighter we can also
include line numbers. We must add the attribute linenums to the listing block in our
markup. This attribute is used by the source highlighters to create and format the line
numbers. We can specify that the line numbers must be generated in table mode or inline
mode. When the line numbers are in table mode we can select the source code without
the line numbers and copy it to the clipboard. If we use inline mode the line numbers
are selectable and are copied together with the selected source code to the clipboard. To
specify which mode we want to use for the line numbers we use the document attribute
coderay-linenums-mode or pygments-linenums-mode depending on the source highlighter
we use. We can use the values table (default) or inline.
= Source code listing
Code listings look cool with Asciidoctor and {source-highlighter}.
[source,groovy,linenums]
---// File: User.groovy
class User {
String username
}
---[source,asciidoc,linenums]
---# Hello world
Asciidoc(tor) is aweseome!
----

Lets generate HTML output from this markup and use different values for the sourcehighlighter and ...-linenums-mode attributes. First we use the Coderay source highlighter
in table mode:

Source code

22

If we use inline mode (:coderay-linenums-mode: inline), we can see the line numbers are
selected as well when we select the source code:

The following screenshots use the Pygments source highlighter with first a table mode for
the line numbers and then inline mode:

Source code

23

Written with Asciidoctor 1.5.0.


Original blog post written on August 13, 2014.

Include Partial Parts from Code Samples


Writing technical documentation with Asciidoc and Asciidoctor is so much fun. Especially
the include macro makes inserting changing content, like source files, a breeze. We only
need to maintain the original source file and changes will automatically appear in the
generated documentation. We can include only a part of source file using tags. In the
source file we add a comment with the following format tag::<tagName>[] to start the
section. We end the section with end::<tagName>[]. Now in our Asciidoc document we
can indicatie the tags we want to include with include::<sourceFile>[tags=<tagName>].
Suppose we have the following Groovy source file Sample.groovy. We want to include the
method hello() in our technical documentation:

Source code

24

// File: Sample.groovy
package com.mrhaki.blog.groovy
class Sample {
// tag::helloMethod[]
String hello() {
'Asciidoc rules!'
}
// end::helloMethod[]
}

In our Asciidoc document we use the following syntax to include only the hello() method:
== Sample Asciidoc
[source,groovy]
.Sample.groovy
---include::Sample.groovy[tags=helloMethod]
----

This will result in the following HTML if we use Asciidoctor with the pretty-print syntax
highlighter:
<div class="listingblock">
<div class="title">Sample.groovy</div>
<div class="content">
<pre class="prettyprint groovy language-groovy"><code>
String hello() {
'Asciidoc rules!'
}
</code>
</pre>
</div>
</div>
</div>

Written with Asciidoctor 0.1.4.


Original blog post written on April 30, 2014.

Include Only Certain Lines from Included Files


In a previous post we learned how to include partial content from included files. We needed
to enclose the content we want to include between start and end tags and reference those
tags in our documentation markup. But Andres Almiray already mentioned in a tweet we
can use line numbers as well:

Source code

25

@marcovermeulen @rfletcherew @mrhaki you can also use -1 in the include


range, like this https://t.co/J4tohY4cp3
Andres Almiray (@aalmiray) June 6, 2014
Lets see this in action in our Asciidoc markup. We can use the include macro and specify
the lines we want to include with the lines attribute. We can include multiple lines by
specifying a range (1..10), or separate different line sections with a ;. To indicate we want
to include lines from a starting line up until the end of the file we can use -1 to indicate
the end of the file.
[source,groovy]
---include::{sourcedir}/Sample.groovy[lines=1;7..-1]
----

Here is the source of the file we want to include:


package com.mrhaki.groovy
/**
* Sample Groovy class with
* two properties.
*/
@groovy.transform.ToString
class User {
String name
String email
}

And when we generate an HTML version of our markup we see that only part of the original
source file is included in the output:

Written with Asciidoctor 0.1.4.


Original blog post written on August 05, 2014.

Explain Code with Callouts


Writing documentation with Asciidoc is such a treat. We can add markers to our code
where we want to explain something in our code. The markers have numbers and are

Source code

26

enclosed in < and > brackets. The explanation for the markers follows a code listing in
a callout list. Here we use the same marker and add extra text to explain the code. We can
put the markers in comments in our code so we can use the markers in existing code.
Suppose we have the following piece of documentation where we add two markers (in
comments) to some Groovy source code:
[source,groovy]
---package com.mrhaki.adoc
class Sample {
String username // <1>
String toString() {
"${username?.toUpperCase() ?: 'not-defined'}" // <2>
}
}
---<1> Simple property definition where Groovy will generate the +setUsername+ and +getUsername+ met\
hods.
<2> Return username in upper case if set, otherwise return +not-defined+.

When we generate this into an HTML document with the prettify syntax highlighter and
icon font we get the following output:

Original blog post written on May 05, 2014.

Highlight Lines In Source Code Listings


In Asciidoctor we can configure syntax highlighting for our source code listings. We can
choose from the built-in support for Coderay, Pygments, highlight.js and prettify. The
syntax highlighter libraries Coderay and Pygments support extra highlighting of lines,
so we can add extra attention to those lines. In this post we see how to use the line
highlighting feature in Asciidoctor.

Source code

27

First we must add the document attribute source-highlighter and use the value coderay or
pygments. When we use Coderay we must also enable the line numbers for the source code
listing, because Coderay will highlight the line numbers in the output. Pygments highlight
the whole line, with or without line numbers in the output. Therefore we choose Pygments
in our example. To highlight certain lines in the source code output we use the highlight
attribute for the source code block. We can specify single line numbers separated by a
comma (,) or semi colon (;). If we use a comma we must enclose the value of the highlight
attribute in quotes. To define a range of line numbers we can define the start and end line
numbers with a hyphen in between (eg. 5-10 to highlight lines 5 to 10). To unhighlight a
line we must prefix it with a exclamation mark (!). For example the following value for the
highlight attribute highlights the lines 2, 3 to 7 and not 5: [source,highlight=1;3-7;!5].
In the following example markup we have a source code block where we highlight the lines
7 to 9. We use Pygments as syntax highlighter:
= Source highlight lines
:source-highlighter: pygments
:pygments-style: emacs
:icons: font
== Creating an application
To create a simple Ratpack application we write
the following code:
.Simple Groovy Ratpack application
[source,groovy,linenums,highlight='7-9']
---package com.mrhaki
import static ratpack.groovy.Groovy.ratpack
ratpack {
handlers {
get {
render "Hello World!" // <1>
}
}
}
---<1> Render output

If we look at the generated HTML we can see lines 7, 8 and 9 are differently styled:

Source code

28

Written with Asciidoctor 1.5.4.


Original blog post written on October 05, 2016.

Changing Highlight.js Theme


Asciidoctor is a great tool for writing technical documentation. If we have source code in
the Asciidoc markup we can set the document attribute source-highlighter to pigments,
coderay, prettify and highlightjs. When we use highlight.js we can also add an extra
document attribute highlightjs-theme with the value of a highlight.js theme. If we do not
specify the highlightjs-theme the default theme github is used.
We use the following Asciidoc markup to see how the HTML output is when we transform
the markup using the HTML backend:
:source-highlighter: highlightjs
= Source code listing
Code listings look cool with Asciidoctor and highlight.js with {highlightjs-theme} theme.
[source,groovy]
---// File: User.groovy
class User {
String username
}

Source code

---[source,sql]
---CREATE TABLE USER (
ID INT NOT NULL,
USERNAME VARCHAR(40) NOT NULL
);
----

The following screenshots show the result of applying different themes:

29

Source code

30

For a complete list of all available themes checkout highlight.js on Github.


If we use the Prettify source code highlighter we must use the prettify-theme document
attribute.
Written with Asciidoctor 1.5.0.
Original blog post written on August 13, 2014.

Source Syntax Highlighting With Prism


Asciidoctor has built-in support for a couple of source syntax highlighting libraries like
Coderay, Pygments, highlight.js and prettify. In this post we learn how to use the Javascript
library Prism to do the syntax highlighting for our source blocks. Because Prism is a
Javascript library we must remember this only works for the HTML backend of Asciidoctor.

Source code

In the following markup we have two source code listings in Java and Groovy:
= Source highlights with Prism
// Set default language for source code listings to java.
:source-language: java
// Include docinfo.html for Prism CSS file and
// include docinfo-footer.html for Prism Javascript.
:docinfo1:
== Creating an application
To create a simple Ratpack application we write
the following code:
.Simple Java Ratpack application
[source]
---package com.mrhaki;
import ratpack.server.RatpackServer;
public class Main {
public static void main(String... args) throws Exception {
RatpackServer.start(server ->
server
.handlers(chain ->
chain
.get(ctx -> ctx.render("Hello World!"))));
}
}
---.Simple Groovy Ratpack application
[source,groovy]
---package com.mrhaki
import static ratpack.groovy.Groovy.ratpack
ratpack {
handlers {
get {
render "Hello World!"
}
}
}
----

Each source listing is transformed to HTML with the following structure:

31

Source code

32

<pre class="highlight"><code class="language-{language}" data-lang="{language}">


{code}
</code></pre>

This fits perfectly with Prism. Prism expects this format to apply the syntax highlighting.
So we only have to add the Prism Javascript and CSS files to the generated HTML file. We
download the Prism Javascript and CSS file from the Prism download site. We save the
Javascript file as prism.js and the CSS file as prism.css. Next we create a docinfo.html to
include a reference to the prism.css file:
<link href="prism.css" rel="stylesheet" />
<style>
/* Override Asciidoctor CSS to get the correct background */
.listingblock pre[class^="highlight "] {
background: #272822;
}
</style>

We also create the file docinfo-footer.html to reference prism.js:


<script src="prism.js"></script>

In our markup we have the document attribute docinfo1 set. This means the files docinfo.html
and docinfo-footer.html are included in the generated HTML output. Lets see the result
in a web browser:

Source code

Written with Asciidoctor 1.5.4.


Original blog post written on October 04, 2016.

33

Extensions
Use Asciidoctor Diagram with Gradle
Since Asciidoctor 1.5.0 we can use extensions when we write AsciiDoc documents. These
extensions can be written in Ruby or any JVM language. If we use Gradle and the Asciidoctor Gradle plugin to transform our documents we can use both extensions. Of course an
extension written in for example Java or Groovy can be included as a regular dependency in
our build configuration. As the extension is on the classpath of the asciidoctor task then it
can be used. When the extension is a Ruby extension and available as a Ruby gem we need
some more configuration in our build file. To use an Asciidoctor extension written in Ruby
in our build we must first make sure that we can download the extension. Then we can
configure the asciidoctor task from the Asciidoctor Gradle plugin to use the extension.
Lets start with a sample Asciidoctor document which uses the Asciidoctor Diagram
extension. With this extension we can embed diagrams as text in our document and get
graphical images as output.
= Sample diagram
:imagesdir: images
== Ditaa
// Sample diagram from Asciidoctor Diagram website
// http://asciidoctor.org/docs/asciidoctor-diagram/
[ditaa, "asciidoctor-diagram-process"]
....
+-------------+
| Asciidoctor |-------+
|
diagram
|
|
+-------------+
| PNG out
^
|
| ditaa in
|
|
v
+--------+
+--------+----+
/---------------\
|
|---+ Asciidoctor +--->|
|
| Text |
+-------------+
|
Beautiful
|
|Document|
|
!magic!
|
|
Output
|
|
{d}|
|
|
|
|
+---+----+
+-------------+
\---------------/
:
^
|
Lots of work
|
+-----------------------------------+
....

Next we create our Gradle build file. We must apply the Asciidoctor Gradle plugin so we
can transform our AsciiDoc documents. And we must apply the JRuby Gradle plugin so we

Extensions

35

can download Ruby gems and use them from the asciidoctor task. The JRuby plugin adds
a gems dependency configuration, which we can use to define the Ruby gems we need. The
task jrubyPrepareGems is also added by the plugin and this task will download the gems and
extract them in our project build directory. In our asciidoctor task that is added by the
Asciidoctor plugin we use the requires property to specify which gem is needed. We also
set the gemPath property to the directory which contains the downloaded and extracted
Ruby gems. The following build file defines the plugins and configures the tasks we need
so we can use Asciidoctor Diagram:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'org.asciidoctor:asciidoctor-gradle-plugin:1.5.0'
classpath 'com.github.jruby-gradle:jruby-gradle-plugin:0.1.5'
}
}
// Plugin so we can render AsciiDoc documents.
apply plugin: 'org.asciidoctor.gradle.asciidoctor'
// Plugin so we can use Ruby gems when rendering documents.
apply plugin: 'com.github.jruby-gradle.base'

dependencies {
// gems dependency configuration is added by
// jruby-gradle-plugin. Here we define
// the gems we need in our build.
gems 'rubygems:asciidoctor-diagram:1.2.0'
}
asciidoctorj {
// We can change the AsciidoctorJ
// dependency version.
version = '1.5.1'
}
asciidoctor {
// jrubyPrepareGems task is added by the JRuby
// plugin and will download Ruby gems we have
// defined in the gems dependency configuration.
dependsOn jrubyPrepareGems
// Asciidoctor task needs the
// asciidoctor-diagram gem, we installed
// with the jrubyPrepareGems task and
// gems dependency configuration.
requires = ['asciidoctor-diagram']
// Set path to find gems to directory
// used by jrubyPrepareGems task.
gemPath = jrubyPrepareGems.outputDir

Extensions

36

}
defaultTasks 'asciidoctor'

To generate our sample document we only have to run $ gradle from the command line.
After the documents are generated we can the result in the build/asciidoc directory of
our project. The following screenshot shows the generated HTML:

Written with Asciidoctor 1.5.1 and Gradle 2.1.


Original blog post written on November 12, 2014.

Write Extensions Using Groovy (or Java)


We can write extension for Asciidoctor using the extension API in Groovy (or any other
JVM language) if we run Asciidoctor on the Java platform using AsciidoctorJ. Extensions
could also be written in Ruby of course, but in this post we see how to write a simple inline
macro with Groovy.
The extension API has several extension points (Source):
Preprocessor: Processes the raw source lines before they are passed to the parser
Treeprocessor: Processes the Document (AST) once parsing is complete
Postprocessor: Processes the output after the Document has been rendered, before
its gets written to file
Block processor: Processes a block of content marked with a custom style (i.e., name)
(equivalent to filters in AsciiDoc)
Block macro processor: Registers a custom block macro and process it (e.g., gist::12345[])

Extensions

37

Inline macro processor: Registers a custom inline macro and process it (e.g., btn:[Save])
Include processor: Processes the include::[] macro
To write an extension in Groovy (or Java) we must write our implementation class for a
specific extension point and we must register the class so AsciidoctorJ knows the class
can be used. Registering the implementation is very simple, because it is using the Java
Service Provider. This means we have to place a file in the META-INF/services directory on
the classpath. The contents of the file is the class name of the implementation class.
Lets start with the Asciidoc markup and then write an implementation to process the inline
macro twitter that is used:
= Groovy Inline Macro
Sample document to show extension for Asciidoctor written in Groovy.
// Here we use the twitter: macro.
// The implementation is done in Groovy.
With the twitter macro we can create links to the user's Twitter page like twitter:mrhaki[].

To implement an inline macro we create a new class and extend InlineMacroProcessor. We


override the process method to return the value that needs to replace the inline macro in
our Asciidoc markup.
// File: src/main/groovy/com/mrhaki/asciidoctor/extension/TwitterMacro.groovy
package com.mrhaki.asciidoctor.extension
import org.asciidoctor.extension.*
import org.asciidoctor.ast.*
import groovy.transform.CompileStatic
@CompileStatic
class TwitterMacro extends InlineMacroProcessor {
TwitterMacro(final String name, final Map<String, Object> config) {
super(name, config)
}
@Override
protected Object process(final AbstractBlock parent,
final String twitterHandle, final Map<String, Object> attributes) {
// Define options for an 'anchor' element.
final Map options = [
type: ':link',
target: "http://www.twitter.com/${twitterHandle}".toString()
] as Map<String, Object>
// Prepend twitterHandle with @ as text link.
final Inline inlineTwitterLink = createInline(parent, 'anchor', "@${twitterHandle}",
attributes, options)

Extensions

38

// Convert to String value.


inlineTwitterLink.convert()
}
}

We have the implementation class so now we can register the class with Asciidoctor. To
register our custom extensions we need to implement the ExtensionRegistry interface. We
implement the register method where we can couple our extension class to Asciidoctor.
// File: src/main/groovy/com/mrhaki/asciidoctor/extension/TwitterMacroExtension.groovy
package com.mrhaki.asciidoctor.extension
import org.asciidoctor.extension.spi.ExtensionRegistry
import org.asciidoctor.extension.JavaExtensionRegistry
import org.asciidoctor.Asciidoctor
import groovy.transform.CompileStatic
@CompileStatic
class TwitterMacroExtension implements ExtensionRegistry {
@Override
void register(final Asciidoctor asciidoctor) {
final JavaExtensionRegistry javaExtensionRegistry = asciidoctor.javaExtensionRegistry()
javaExtensionRegistry.inlineMacro 'twitter', TwitterMacro
}
}

The class that registers our extension must be available via the Java Service Provider so
it is automatically registered within the JVM used to run Asciidoctor. Therefore we need
to create the file META-INF/services/org.asciidoctor.extension.spi.ExtensionRegistry
with the following contents:
# File: src/main/resources/META-INF/services/org.asciidoctor.extension.spi.ExtensionRegistry
com.mrhaki.asciidoctor.extension.TwitterMacroExtension

We have taken all steps necessary to have our inline macro implementation. We must
compile the Groovy classes and add those with the Java Service Provider file to the
classpath. We can package the files in a JAR file and define a dependency on the JAR file in
our project. If we use Gradle and the Gradle Asciidoctor plugin we can also add the source
files to the buildSrc directory of our project. The files will be compiled and added to the
classpath of the Gradle project.
With the following Gradle build file we can process Asciidoc markup and execute the
twitter inline macro. We store the source files in the buildSrc directory.

Extensions

39

buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'org.asciidoctor:asciidoctor-gradle-plugin:1.5.0'
}
}
apply plugin: 'org.asciidoctor.gradle.asciidoctor'

The build file in the buildSrc directory has a dependency on AsciidoctorJ. This module
makes it possible to run Asciidoctor on the JVM.
// File: buildSrc/build.gradle.
apply plugin: 'groovy'
repositories {
jcenter()
}
dependencies {
compile 'org.asciidoctor:asciidoctorj:1.5.0'
}

Lets see part of the HTML that is generated if we transform the Asciidoc markup that is
shown at the beginning of this blog post. The twitter inline macro is transformed into a
link to the Twitter page of the user:
...
<div class="paragraph">
<p>With the twitter macro we can create links to the user's Twitter page like <a href="http://www\
.twitter.com/mrhaki">@mrhaki</a>.</p>
</div>
...

Andres Almiray also wrote about writing extensions with Gradle.


Written with Asciidoctor 1.5.0 and Gradle 2.0.
Original blog post written on August 28, 2014.

Use Inline Extension DSL with Gradle


One of the great features of Asciidoctor is the support for extensions. If we want to have
some special feature we want to use, but is not supported by Asciidoctor, we can add our
own extension. On the Java platform we can write those extensions in for example Java and
Groovy. When we use Gradle as the build tool with the Asciidoctor plugin we can write the
code for the extension in our Gradle build file with the Groovy extension DSL.
Suppose we want to write a new inline macro that will transform the following markup
issue:PRJ-100[] into a link that points to the web page for issue PRJ-100. First we create
our Asciidoctor source document:

40

Extensions

= Sample
This is an issue issue:PRJ-100[].

Now we write the following Gradle build file. First we include the Gradle Asciidoctor plugin.
Then we can use the extensions configuration method to add our code for the inline
macro. If we would write another type of extension we could still use the same place to
add it, but then we dont use inlinemacro.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'org.asciidoctor:asciidoctor-gradle-plugin:1.5.2'
}
}
// Apply the Asciidoctor Gradle plugin.
apply plugin: 'org.asciidoctor.convert'

asciidoctor {
// Here we can add the code for extensions we write.
extensions {
// Implementation for inline macro to replace
// issue:<issue-id>[] with a link to the issue.
inlinemacro (name: "issue") {
parent, target, attributes ->
options = [
"type": ":link",
"target": "http://issue-server/browse/${target}".toString()
]
// Create the link to the issue.
createInline(parent, "anchor", target, attributes, options).render()
}
}
}

Lets transform our sample Asciidoctor markup to HTML. We see the following result:

Extensions

41

The link that is generated is http://issue-server/browse/PRJ-100.


Written with Gradle 2.3 and Asciidoctor 1.5.2.
Original blog post written on March 04, 2015.

Using Ruby Extensions With Asciidoctor Gradle Plugin


Asciidoctor is a Ruby tool, but luckily we can use AsciidoctorJ to use Asciidoctor in Java
code. The Asciidoctor Gradle plugin relies on AsciidoctorJ to run. AsciidoctorJ allows us
to write custom extensions in Java (or Groovy), but we can still use Asciidoctor extensions
written in Ruby with the Gradle plugin.
In the following example we use the emoji-inline-macro from Asciidoctor extensions lab.
This is an extension written in Ruby. We create a new directory for our sample and create
a lib folder. Inside the lib directory we copy the file emoji-inline-macro.rb and the
supporting directory emoji-inline-macro. These files are all in the Asciidoctor extensions
lab repository. After we have copied the files we should have the following structure:
$ tree lib/
lib/
emoji-inline-macro

extension.rb

sample.adoc

twemoji-awesome.css
emoji-inline-macro.rb
1 directory, 4 files

In our build file we configure the asciidoctor task and use the requires method to define
a dependency on the Ruby emoji-inline-macro.rb file:
// File: build.gradle
plugins {
id 'org.asciidoctor.convert' version '1.5.3'
}
asciidoctor {
// Add requirement on Ruby extension.
requires './lib/emoji-inline-macro.rb'
}

We are done. Next we create a sample Asciidoc markup document with the following
content:

Extensions

= Asciidoctor is awesome!
Writing with Asciidoctor makes me feel emoji:sunny[5x]
This sample is written while Devoxx BE is in progress, so
@DevoxxPeople: Enjoy your emoji:beers[]

We get the following HTML5 output when we run the asciidoctor task:

Written with Asciidoctor Gradle plugin 1.5.3.


Original blog post written on November 08, 2016.

42

Miscellaneous
Using Comments
We can add comments to our Asciidoc markup. The comments will not be added to
generated output. We can add both single and multiline comments in the markup. Single
line comments start with a double slash (//). Multiline comments are enclosed in a block
of four forward slashes (////).
The following sample markup defines Asciidoc markup with comments:
= Asciidoctor comments
// Paragraph with some text.
Writing documentation is fun with Asciidoctor.
* Item A
* Item B
// Divide lists with a single line comment.
// Now we have two lists, otherwise it would
// be a single list with 4 items.
* Item 1
* Item 2
////
With four forward slashed we can
start a multiline comment.
And we close it with another
four forward slashes.
////
Asciidoc is really like _programming_ documentation.

When we generate HTML output we dont see any of the comments, not even in the HTML
source:

Miscellaneous

44

Written with Asciidoctor 1.5.0.


Original blog post written on August 12, 2014.

Which Asciidoctor Version is Used?


When we create documentation using Asciidoc and Asciidoctor we can access the built-in
attribute asciidoctor-version to see which version of Asciidoctor is used to generate the
documentation.
We can reference built-in attributes like any other attributes, so we enclose the name of
the attributes with curly braces ({attributeName}). In the following sample we simply print
out the Asciidoctor version that was used to generate the content:
.Use built-in attribute asciidoctor-version
Document generated with Asciidoctor {asciidoctor-version}.

And we get the following HTML content once we have generated HTML from our Asciidoc
source code:

Code written Asciidoctor 0.1.4.


Original blog post written on June 04, 2014.

Disable Last Updated Text in Footer


When we transform our Asciidoc source files to HTML Asciidoctor will print the date and
time the document was last updated in the footer. If we want to disable the Last updated
text we disable the document attribute last-update-label.
In the following example Asciidoc file we disable the Last update label in the footer:

Miscellaneous

45

:last-update-label!:
= Change footer
To disable the _Last updated_ text
in the footer we disable the document
attribute `last-update-label`.
---// Disable last updated text.
:last-update-label!:
----

When we transform this to HTML we get the following output:

Written with Asciidoctor 1.5.1.


Original blog post written on November 26, 2014.

Adding Custom Content to Head and Footer


When we convert our Asciidoctor markup to HTML we automatically get a head and footer
element. We can add custom content to the HTML head element and to the HTML div with
id footer. We must set a document attribute and create the files that contain the HTML
that needs to be added to the head or footer. We have three different document attributes
we can set to include the custom content files:
:docinfo: include document specific content. Files to be included must be named
<docname>-docinfo.html for head element and <docname>-docinfo-footer.html for
footer content.
:docinfo1: include general custom content. Files to be included must be named
docinfo.htmtl for head element and docinfo-footer.html for footer content.
:docinfo2: include document specific and general custom content. Files to be
included must be named <docname>-docinfo.html and docinfo.html for head element
and <docname>-docinfo-footer.html and docinfo-footer.html for footer content.
In this sample we create the files docinfo.html and docinfo-footer.html we want to
include in the generated output from the following Asciidoctor source file:

Miscellaneous

= Asciidoctor custom header and footer


Hubert A. Klein Ikkink
// Document specific and general custom
// content files are used:
:docinfo2:
// Include general custom content files:
//:docinfo1:
// Include document specific content files:
//:docinfo:
// In generated HTML this is transformed
// to <meta name="description" content="..."/>
:description: Sample document with custom header and footer parts.
// In generated HTML this is transformed
// to <meta name="keywords" content="..."/>
:keywords: Asciidoctor, header, footer, docinfo
Using the `docinfo` attributes we can include custom content
in the header and footer. Contents of the files
named `docinfo.html` and `docinfo-footer.html` are included.
We can choose between general or document specific custom
header and footer content.

Our docinfo.html looks like this:


<!-- Change some CSS. -->
<style>
/* Change CSS overflow for table of contents. */
#toc.toc2, #toc { overflow: scroll; }
/* Change styling for footer text. */
.footer-text { color: rgba(255,255,255,.8); }
</style>
<!-- We could also include Javascript
for example in this document. -->

For the custom footer we create the file docinfo-footer.html:


<p class="footer-text">
<!-- We can use document attributes: -->
Generated with Asciidoctor v{asciidoctor-version}.
</p>

The following screenshot shows the generated HTML page:

46

Miscellaneous

47

And here is part of the generated head element:


<head>
...
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 1.5.2">
<meta name="description" content="Sample document with custom header and footer parts.">
<meta name="keywords" content="Asciidoctor, header, footer, docinfo">
<meta name="author" content="Hubert A. Klein Ikkink">
<title>Asciidoctor custom header and footer</title>
...
<!-- Change some CSS. -->
<style>
/* Change CSS overflow for table of contents. */
#toc.toc2, #toc { overflow: scroll; }
/* Change styling for footer text. */
.footer-text { color: rgba(255,255,255,.8); }
</style>
<!-- We could also include Javascript
for example in this document. -->
</head>

Written with Asciidoctor 1.5.2.


Original blog post written on April 20, 2015.

Include Raw HTML


If we use the HTML backend with Asciidoc we can use a passthrough block to include raw
HTML in the output. The contents of a passthrough block is untouched and will be put
literally in the generated output. A passthrough block is enclosed in four plus signs (++++).

Miscellaneous

48

In the following sample Asciidoc markup we include some Javascript:


++++
<p><span id="replaceMe">Sample content</span> replaced by Javascript</p>
<script>
document.getElementById('replaceMe').innerHTML = 'New content!'
</script>
++++

The generated HTML will execute the Javascript and we get the following output:

We can also add raw HTML inline in our Asciidoc markup. We can enclose the HTML in
triple plus signs (+++) or use the pass:[] macro. The following sample shows the markup
where both methods are used:
We can also use passthrough inline macros to have raw HTML in the output.
For example with three plus symbols we can +++<em>emphasize text</em>+++.
Or we can use the inline macro syntax with the +pass+ name to pass:[<strong>make text strong</str\
ong>].

The generated HTML looks like this:

Code written with Asciidoctor 0.1.4.


Original blog post written on June 11, 2014.

Use Inline Icons


Asciidoctor adds the icon: macro to the Asciidoc markup. With the macro we can insert an
icon in our text. We specify the name of the icon after the macro name. If we use an HTML
backend together with the document attribute icons set to the value font we can use
Font Awesome Icons. If we want to use icon images the icon is looked up in the directory
specified by the attribute iconsdir. For example the Docbook backend will use this to
insert an icon.
In the following markup we use the icon macro to insert a comment and file icon:

Miscellaneous

49

:icons: font
icon:comment[] This is a comment icon
icon:file[] And a file icon

We get the following result:

And the following HTML is generated:


<div class="paragraph">
<p><span class="icon"><i class="icon-comment"></i></span> This is a comment icon</p>
</div>
<div class="paragraph">
<p><span class="icon"><i class="icon-file"></i></span> And a file icon</p>
</div>

We can specify CSS classes using the role attribute for the macro. But together with the
HTML backend and font-based icons we can also use other attributes: size, rotate and
flip. The size attribute can specified without the attribute name if we use it as the first
attribute:
// Change icon size
icon:comment[4x] This is a comment icon
// Alternative icon:comment[size="4x"]
// Possible values: large, 2x, 3x, 4x, 5x
// Flip and rotate
icon:file[flip="vertical", rotate="180", role="lime"] And a file icon
// Possible flip values: vertical, horizontal
// Possible rotate values: 90, 180, 270

When we generate HTML we get the following result:

When we want to use an inline icon as a link we can use the attributes link and window:

Miscellaneous

50

// Use link attribute to specify link.


// Optional window attribute will be the target window.
icon:user[link="http://www.mrhaki.com/about", window="_blank"]

Image based icons have the following attributes, like an image: alt, width, height, title,
role.
Code written and generated with Asciidoctor 0.1.4.
Original blog post written on June 19, 2014.

Changing the FontAwesome CSS Location


To use font icons from FontAwesome we set the document attribute icons with the value
font. The default link to the CSS location is https://cdnjs.cloudflare.com/ajax/libs/fontawesome/4.1.0/css/font-awesome.min.css. We can change the location for the FontAwesome CSS with document attributes.
If we want to use a different CDN to serve the CSS we can set the document attribute
iconfont-cdn and set the URI as a value:
:icons: font
// Set new URI for reference to FontAwesome CSS
:iconfont-cdn: //maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css
== Sample doc

To reference the FontAwesome CSS from a relative location from our generated HTML
page we can first unset the attribute iconfont-remote and set the attribute iconfont-name:
:icons: font
// First unset attribute to remotely link FontAwesome CSS
:iconfont-remote!:
// Specify name of FontAwesome CSS.
:iconfont-name: fontawesome-4.1.0
// We can optionally set the directory where CSS is stored.
:stylesdir: css
== Sample doc

In the generated HTML source we see the following link element:


...
<link rel="stylesheet" href="css/fontawesome-4.1.0.css">
...

Written with Asciidoctor 1.5.0.


Original blog post written on August 15, 2014.

Miscellaneous

51

Change URI Scheme for Assets


When we define the document attribute icons with the value font the FontAwesome fonts
are loaded in the generated HTML page. In the head section of the HTML document a link
element to the FontAwesome CSS on https://cdnjs.cloudflare.com/ajax/libs is added.
Also when we use the highlight.js or Prettify source highlighter a link to the Javascript files
on the cdnjs.cloudflare.com server is generated. We can change the value of the scheme
from https to http by setting the attribute asset-uri-scheme to http. Or we can leave out
the scheme so a scheme-less URI is generated for the links. A scheme-less URI provides the
benefit that the same protocol of the origin HTML page is used to get the CSS or Javascript
files from the cdnjs.cloudflare.com server. Remember this might provide a problem if the
HTML page is opened locally.
In the next sample Asciidoc markup we change the scheme to http:
:asset-uri-scheme: http
:icons:font
== Asset URI Scheme
Sample document.

In the generated HTML we see the new scheme value:


<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.1.0/css/font-aw\
esome.min.css">

Now we leave the value of the asset-uri-scheme attribute empty:


:asset-uri-scheme:
:icons:font
== Asset URI Scheme
Sample document.

The generated HTML now contains a link to the FontAwesome CSS with a scheme-less
URI:
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.1.0/css/font-awesome\
.min.css">

Written with Asciidoctor 1.5.0.


Original blog post written on August 15, 2014.

Miscellaneous

52

Replacements For Text To Symbols


With Asciidoctor we can use text to describe a symbol in our markup. The text is
automatically transformed to a Unicode replacement. For example if we use the text (C) it
is converted to (C) which is the copyright symbol: (C).
In the following sample we see all the symbol replacements:

Written with Asciidoctor 1.5.2.


Original blog post written on September 15, 2016.

Turn Section Titles Into Links


When we use Asciidoctor to write and generate our documentation we can use the
document attributes sectanchors or sectlinks to turn section titles into link references.
This can be useful if we want for example to bookmark a section.
The sectanchors attribute will show a section icon in front of the section title when we
hover over the title. The icon itself is the link to the section.

Section title in generated HTML output.

Hover over section title and icon is shown.

Miscellaneous

53

Section icon is link reference.


With the sectlinks attribute the section title itself is the link. So we dont get an icon if we
hover of the title, but the title text itself is now the link.

The section title is now a link.


Original blog post written on May 23, 2014.

Leave Section Titles Out of Table Of Contents


Section titles in our document (titles start with two or more equals signs) are part of the
document hierarchy and therefore can be used in a generated table of contents. If we dont
want to include a section title in the table of contents we must make the title discrete. The
title is styled like a normal section title, but it is no longer part of the document structure
as title. Therefore the section title will not be generated in the table of contents. To make
a title discrete we must use the attribute discrete for the title.
In the following document we first have a simple document with two section titles. When
we generate the HTML for this document we see both titles in the table of contents.
:toc:
= Section example
== Introduction
Simple introduction section.
== More
There is more information in this document.

Miscellaneous

54

Now we make the first section title discrete by applying the discrete attribute:
:toc:
= Section example
[discrete]
== Introduction
Simple introduction section.
== More
There is more information in this document.

We generate the document again as HTML and this time we see the section title is no
longer in the table of contents:

Miscellaneous

55

Written with Asciidoctor 1.5.2.


Original blog post written on September 10, 2015.

Use Captions For Listing Blocks


Asciidoctor has some built-in attributes to work with captions for certain content blocks.
For example the table-section attribute defines the caption label (by default Table) that
is prefixed to a counter for all tables in the document. When we transform our markup
Asciidoctor will insert the text Table followed by the table number. By default the caption
for listing blocks is disabled, but we can easily enable it with the listing-caption attribute.
In the following markup we enable the caption for listing blocks and set the value to
Listing. This will add the text Listing followed by the listing section counter to the output.
= Code examples
// Enable numbered captions
// for listing blocks.
// We define the constant Listing
// as a caption value. This will be
// followed by a counter.
:listing-caption: Listing
== Creating an application
To create a simple Ratpack application we write
the following code:
.Simple Java Ratpack application
[source,java]
---package com.mrhaki;

Miscellaneous

56

import ratpack.server.RatpackServer;
public class Main {
public static void main(String... args) throws Exception {
RatpackServer.start(server ->
server
.handlers(chain ->
chain
.get(ctx -> ctx.render("Hello World!"))));
}
}
---.Simple Groovy Ratpack application
[source,groovy]
---package com.mrhaki
import static ratpack.groovy.Groovy.ratpack
ratpack {
handlers {
get {
render "Hello World!"
}
}
}
----

When we generate the HTML for this markup we see the caption for the two listing blocks:

Miscellaneous

57

To disable the listing captions we must negate the document attribute: :!listing-caption:.
Written with Asciidoctor 1.5.4.
Original blog post written on September 26, 2016.

Change Level Offset For Included Documents


When we use the include directive to include another document we can must make sure
the included document fits the levels of our main document. For example the included
document shouldnt have level 0 headings if the main document already contains a level
0 heading. We can change the level offset in the main document before including another
document. This will change the heading levels for the included document so all heading
rules are okay.
To change the level offset we use the document attribute leveloffset. It is best to use a
relative value, so if the included document also contains included document the output
will still be okay and the heading rules still apply. Alternatively we can use the leveloffset

Miscellaneous

58

attribute for the include directive. In the following sample document we include other
files with a level 0 heading:
// File: book.adoc
= Include other files
// Include section numbers.
:sectnums:
// The file `chapter1.adoc` has a level 0 heading.
// To make sure it can be included we
// increase the level offset.
:leveloffset: +1
include::chapter1.adoc[]
// Reset level offset.
:leveloffset: -1
// Or use :leveloffset: 0
// We can also use the `leveloffset` attribute
// of the `include` directive. The level offset
// is then automatically reset.
include::chapter2.adoc[leveloffset=+2]

Here is the source for the included files:


// File: chapter1.adoc
= First chapter
Sample document to be included.

// File: chapter2.adoc
= First chapter
Sample document to be included.

When we transform book.adoc to HTML with Asciidoctor we get the following result:

Written with Asciidoctor 1.5.4.


Original blog post written on September 20, 2016.

Miscellaneous

59

Use Counters in Markup


In Asciidoctor we can create a document attribute as a counter attribute. The attribute
is automatically incremented each time we use it in our markup. We can choose to use
numbers or characters. Only latin characters from a to z or A to Z are allowed. By default
the counter will start at 1, but we can define another start value when we use the counter
attribute for the first time.
To define a counter attribute we must prefix the attribute name with counter:. Each time
we use this syntax with the counter: prefix the value is incremented and displayed. To only
display the current value, without incrementing, we simply refer to the document attribute
without the counter: prefix. For example if we want to add a counter attribute with the
name steps we would use the following markup in Asciidoctor: {counter:steps}.
To increment the counter without display it we must replace counter: with counter2:. The
value of the attribute is incremented but not displayed. So to increment our steps attribute
we would use the syntax: {counter2:steps}. To get the current value without incrementing
we simply use {steps}.
To start with a different value than 1 we can suffix the attribute name with :&lt:startvalue>. Lets look at how we would create the steps counter attribute starting from 100:
{counter:steps:100}. To have a counter with letters we define the start value as a letter
from which we want to count: {counter:steps:A}.
In the following example markup we see different usages of the counter support in
Asciidoctor:
== Counters
In Asciidoctor we can use counters. To use them
we ({counter:usage}) use a document attribute
prefixed with `counter:` and ({counter:usage}) use it again
to increment the counter.
Instead of numbers we can use characters. To use them
we ({counter:usageChar:A}) use a document attribute
prefixed with `counter:` and suffix `:A` and ({counter:usageChar}) use it again
to increment the counter.
=== Current value
Current value for a counter can be obtained by just referring to document attribute name.
Value counter is *{usage}*.
=== Increment
{counter2:usage} We can also increment the counter without displaying the value.
On the next increment the value is *{counter:usage}*.
=== Start at
To start at another number than 1 we can specify the starting counter value as
a suffix to the counter attribute as `:<start>`.
{counter:sample:10}. do something followed by {counter:sample}. something else.

Miscellaneous

60

When we transform this to HTML with Asciidoctor we get the following result:

Written with Asciidoctor 1.5.4.


Original blog post written on September 16, 2016.

Links Without URI Scheme


Since Asciidoctor 1.5.0 we can use the document attribute hide-uri-scheme to turn URLs
into links, where the link text is displayed without the URI scheme. This can save typing
when we simply want to add a URL without any special description.
In the next Asciidoc syntax we first define URLs without the hide-uri-scheme attribute,
followed by URLs after the attribute is set:
Reference to http://www.mrhaki.com is
turned into link +
`+<a href="http://www.mrhaki.com">http://www.mrhaki.com</a>+`.
To loose URI scheme we could write the link
as http://www.mrhaki.com[www.mrhaki.com]. Or we
can use the new attribute `hide-uri-scheme` which is
added to Asciidoctor 1.5.0.
:hide-uri-scheme:

Miscellaneous

61

After applying the `hide-uri-scheme` attribute


the URI scheme is removed from the text in links.
So a reference to http://www.mrhaki.com is
turned into the link +
`+<a href="http://www.mrhaki.com">www.mrhaki.com</a>+`
This also works for other URI schemes like `file`.
For example
file:///Users/mrhaki/file.txt is translated to +
`+<a href="file:///Users/mrhaki/file.txt">/Users/mrhaki/file.txt</a>+`.

When we generate output using the HTML backend we see the following output:

Written with Asciidoctor 1.5.0.


Original blog post written on August 12, 2014.

Use Link Attributes


To define a link in Asciidoc markup we only have to type the URL followed by an optional
text for the link in square brackets ([text link]). With Asciidoctor we can add extra
attributes that can be used when the content is generated. We have to set the document
attribute :linkattrs: to make sure Asciidoctor will process the attributes.
In the following sample Asciidoc markup we define links with attributes like window and
role:
:linkattrs:
http://mrhaki.blogspot.com[Messages from mrhaki, window="_blank"]
// Because window=_blank is used often we can
// use the shortcut ^.
http://mrhaki.blogspot.com[Messages from mrhaki^, role="ext-link"]

The following HTML is generated when we use Asciidoctor with the HTML backend:

Miscellaneous

62

<div class="paragraph">
<p><a href="http://mrhaki.blogspot.com" target="_blank">Messages from mrhaki</a></p>
</div>
<div class="paragraph">
<p><a href="http://mrhaki.blogspot.com" class="ext-link" target="_blank">Messages from mrhaki</a>\
</p>
</div>

Code written with Asciidoctor 0.1.4.


Original blog post written on June 13, 2014.

Customize How Missing Attributes Are Handled


Document attributes are like variables for your Asciidoctor document. Normally when we
reference an attribute that is not set in our Asciidoctor markup the reference is still in the
output. This is very handy, because we immediately see that a document attribute is not set.
But we can customize this behavior with the document attribute attribute-missing. We
can use the default value skip, which leaves the reference in the output. Another option is
drop, which means the reference is dropped from the output. Finally we can set the value
to drop-line, where the complete line with the attribute reference is dropped from the
output.
In the following sample Asciidoctor markup we set the three values for the attribute
attribute-missing:
== Handle Missing Attributes
:attribute-missing: skip
.`:attribute-missing: skip`
Line with attribute {sample-attr}, should show attribute reference.
:attribute-missing: drop
.`:attribute-missing: drop`
Line with attribute {sample-attr}, drops attribute reference.
:attribute-missing: drop-line
.`:attribute-missing: drop-line`
Line with attribute {sample-attr}, is completely dropped.

When we transform this to HTML5 we get the following output:

Miscellaneous

63

Written with Asciidoctor 1.5.2.


Original blog post written on February 25, 2015.

Substitute Attribute in Listing Block


To write a listing block where the contents of the block is generated in a monospace font
and line breaks are preserved is easy with Asciidoc. We can use ---- or [listing] or indent
the paragraph with one space to define a listing block. All content in a listing block is
processed as is, but special characters and callouts are processed. This means if we have
an attribute in our block the attribute is not substituted with the actual value. To enable
the replacement of the attribute with the attribute value we must set the subs attribute
for our listing block.
Suppose we have the following listing block in our documentation with the attribute
grailsVersion in the content:
:grailsVersion: 2.3.8
---$ grails -version
Grails version: {grailsVersion}
----

When we generate HTML output we get the following result:

We add now the subs attribute to our listing:

Miscellaneous

64

:grailsVersion: 2.3.8
[subs="attributes"]
---$ grails -version
Grails version: {grailsVersion}
----

The generated HTML now shows that the attribute value is used:

Alternative ways to define the listing block with the subs attribute. These will render the
same result:
[listing,subs="attributes"]
$ grails -version
Grails version: {grailsVersion}
[subs="attributes"]
$ grails -version
Grails version: {grailsVersion}

Written with Asciidoctor 0.1.4.


Original blog post written on May 23, 2014.

Escape Attribute References


One of the many cool features of Asciidoc is attribute substitution. We can define attributes
with values and in our Asciidoc markup we reference those attributes between curly
braces. For example we could include the value of the attribute customAttr like this in
Asciidoc: {customAttr}. But sometimes we simply want to include some text between curly
braces without any attribute value substitution. We need to put an escape character (\)
before the first brace and Asciidoc will not replace the attribute with a value.
Suppose we have the following Asciidoc, where we want to explain some Groovy syntax
("${sampleValue}"). Asciidoc will try to substitute the attribute sampleValue with a value if
set.
This is a sample where we include +"${}"+ as a Groovy GString sample.
The sample +"${attrValue}"+ should be unchanged in the output.

If we generate HTML with Asciidoctor we get the following output and notice that
attrValue is changed to attrvalue (lower case v):

Miscellaneous

65

If we add the escape character no attribute substitution will take place, even if we assign
a value to the attribute attrValue:
:attrValue: sample
This is a sample where we include +"${}"+ as a Groovy GString sample.
The sample +"$\{attrValue}"+ should be unchanged in the output.

Now we get the output we expect:

Written with Asciidoctor 0.1.4.


Original blog post written on July 03, 2014.

Using Conditional Directives


In Asciidoc markup we can include or exclude text based on the existence of document
attributes or based on the value of a document attribute. Therefore we use the macros
ifdef, ifndef and ifeval. If we want to include some content if the document attribute
sample is set we can use the following syntax:
ifdef::sample[Content is shown when sample attribute is set]
ifdef::sample[]
Content is shown when sample attribute is set
endif::sample[]

If we want to include some content if the attribute is not set we use ifndef:
ifndef::sample[Content is shown when sample attribute is NOT set]
ifndef::sample[]
Content is shown when sample attribute is NOT set
endif::sample[]

We can even use multiple attributes for these macros. If the attribute names are ,
separated only one of the attributes need to be set to include the content. If we use the +
separator all attributes must be set to include the content.
In the following sample Asciidoc markup we see several usages of the ifdef and ifndef
macros:

Miscellaneous

66

|===
| Attributes
|
ifdef::intermediate[:intermediate:]
ifndef::intermediate[:intermediate!:]
|
ifdef::advanced[:advanced:]
ifndef::advanced[:advanced!:]
|===

ifdef::advanced[]
This is only visible if we
set the advanced attribute.
endif::advanced[]
ifdef::intermediate,advanced[]
Here is some content for the
intermediate or advanced readers,
which is visible if the attributes
intermediate or advanced are set.
endif::intermediate,advanced[]
ifdef::intermediate+advanced[]
Here is some content for the
intermediate and advanced readers,
which is visible if the attributes
intermediate AND advanced are set.
endif::intermediate+advanced[]

If we generate HTML output and set and unset the intermediate and advanced document
attributes we see that content is included or not:

Miscellaneous

67

Finally with Asciidoctor we can use the ifeval macro to evaluate the value of attributes.
If the expression evaluates to true the content is included otherwise it is skipped. The
following sample evaluate the version document attribute and shows different content
based on the value:

Miscellaneous

68

|===
| Attributes
|
:version: {version}
|===

ifeval::[{version} >= 1]
We are live!
endif::[]

ifeval::[{version} < 1]
Still developing...
endif::[]

Lets generate HTML with different values for the version attribute:

Written with Asciidoctor 0.1.4.


Original blog post written on August 05, 2014.

Conditional Directive to Check If Document is On GitHub


In a previous blog post we learned about the conditional directives in Asciidoctor. Dan
Allen mentioned a conditional directive that we can use to see if the document is used on
GitHub. The conditional directive is called env-github.
We have the following Asciidoc markup for a document stored on GitHub:

Miscellaneous

69

:blogpost: http://mrhaki.blogspot.com/2014/08/awesome-asciidoc-check-if-document-is.html
= Asciidoc on GitHub
Sample document for {blogpost}[Aweseome Asciidoc blog post].
ifdef::env-github[]
This line is only visible if the document is on GitHub.
GitHub is using Asciidoctor {asciidoctor-version}.
endif::env-github[]
ifndef::env-github[This line is visible if not rendered on GitHub.]

To see what is rendered we can view the document on GitHub.

Written with Asciidoctor 1.5.0.


Original blog post written on August 26, 2014.

Using Document Fragments


Normally all Asciidoc files are processed and transformed to output files by Asciidoctor.
But if we start the file name with an underscore (_) the file is not transformed to an output
file. This is very useful, because we can define some Asciidoc document fragments and
include them in other Asciidoc files, but in the output directory the document fragment is
not generated.
Lets create two Asciidoc files. One is _attrs.adoc which is a document fragment file that
is used in sample.doc:
// File: _attrs.adoc
:blogger_url: http://mrhaki.blogspot.com
:blogger_tag: Aweseome Asciidoctor
:author: mrhaki

// File: sample.adoc
include::_attrs.adoc[]
== Sample

Miscellaneous

70

Asciidoctor handles files starting


with an underscore (`_`) differently. The file is
not processed, but can be used in other Asciidoc
documents.
More {blogger_url}[blog posts] about
{blogger_tag} available written by {author}.

From the command line we can invoke the asciidoctor command. We also check the
directory and see there is only the file sample.html:
$ asciidoctor sample.adoc
$ ls
_attrs.adoc sample.adoc sample.html
$

The following screenshot shows how the sample.html looks like in a web browser:

Written with Asciidoctor 1.5.1.


Original blog post written on November 25, 2014.

Keep Line Breaks in Paragraphs


Normally when we write a paragraph in Asciidoc markup the line breaks are not preserved.
Multiple lines are combined into a paragraph until an empty line is found to separate
paragraphs. If we want to keep line breaks we must add the plus sign (+) at the end of the
line. If we generate HTML from the Asciidoc markup a line break is inserted with a <br>
tag. We can also use the hardbreaks document attribute to enable or disable line breaks in
paragraphs without the addition of the + symbols.
In the following Asciidoc sample we use the + sign to keep line breaks in a paragraph:

Miscellaneous

71

// Use + to keep line breaks in paragraph.


A sample paragraph +
with line breaks +
applied using the _+_ symbol.
A sample paragraph
without line breaks,
because the _+_ symbol
is not used.

We get the following result if we use the HTML backend:

We can also use the hardbreaks attribute:


:hardbreaks:
A sample paragraph
with line breaks.
Although the _+_ symbol
is not used, but the
document attribute
*:hardbreaks:* is set.
// Disable hardbreaks for reminder of document.
:!hardbreaks:

If we generate HTML we get the following result:

Samples written with Asciidoctor 0.1.4.


Original blog post written on June 25, 2014.

Apply Custom Styling to Blocks


To define a listing block to display for example source code in Asciidoc is easy. We can use
---- delimiter or explicitly use [listing] and include the source code. If we use the HTML

Miscellaneous

72

backend to generate HTML the result will be a pre block enclosed in some div sections
with a couple CSS classes applied. If we want to add our own CSS classes to the generated
output we can apply so-called roles to our block.
In the following Asciidoc markup we apply a role with the name console to a listing block.
Remember we can use the same syntax for other types of blocks as well.
[source,role="console"]
---$ ls
index.adoc
$
----

If we generate HTML we get the following HTML code. Notice the class attribute of the
first div contains console:
<div class="listingblock console">
<div class="content">
<pre class="prettyprint"><code>$ ls
index.adoc
$</code></pre>
</div>
</div>

We can use an alternate syntax which resembles CSS classes closely. We can use dot (.)
followed by the role name.
[source.console]
---$ ls
index.adoc
$
----

If we want to apply multiple roles (CSS classes) we can specify the names separated by
spaces in the role attribute or chain the names with .:
[source.console.shell]
---$ ls
index.adoc
$
---// Or use role attribute:
[source,role="console shell"]
---$ ls
index.adoc
$
----

Miscellaneous

73

// If we don't apply the source attribute,


// we can still set roles:
[.console] // Or [role="console"]
---$ ls
index.adoc
$
---// If we do not use the delimiter,
// but specify block type:
[listing.console] // Or [listing,role="console"]
$ ls
index.adoc
$

By specifying role values we can customize how the output looks with different implementation for the CSS classes that we want.
Code generated by Asciidoctor 0.1.4.
Original blog post written on June 16, 2014.

Nested Delimited Blocks


In our Asciidoc markup we can include delimited blocks, like sidebars, examples, listings
and admonitions. A delimited block is indicated by a balanced pair of delimiter characters.
For example a sidebar starts and ends with four asterisk characters (****). If we want to
nest another delimited block of the same type we must add an extra delimiter character
at the start and end of the nested block. So when we want to nest another sidebar block
inside an existing sidebar block we must use five asterisk characters (*****).
In the following example Asciidoc source we have several blocks with nested blocks:
== Nested sidebar
[sidebar]
.Sidebar
****
This is just an example of how
// Start nested block with extra *
*****
a nested delimited block
*****
can be defined.
****

== Nested example block


.Nested example
====

Miscellaneous

Also the example delimited block


// Start nested block
=====
allows nested blocks
======
and nested blocks
======
=====
to be defined.
====
== Nested admonition block
[NOTE]
====
We can nest
[TIP]
=====
adminition blocks
=====
as well.
====
== Mixing delimited blocks
[NOTE]
====
Of course we can also define
****
another delimited block
*****
with nested block
*****
****
inside delimited blocks.
====

If we transform this to HTML we get the following result:

74

Miscellaneous

75

Miscellaneous

76

Written with Asciidoctor 1.5.2.


Original blog post written on December 11, 2014.

Do Not Wrap Lines in Listing or Literal Blocks


When we use listing or literal blocks in our Asciidoc markup long lines will be wrapped
if needed in the generated output. We can use the options attribute with a nowrap value
to have horizontal scrolling instead of wrapped lines. This applies for the HTML backend
when we generate the documentation.
In the following markup we first use the default wrapping of lines:
[source,groovy]
---class GroovyHelloWorld {
def sayHello(final String message = 'world') { // Set default argument value for message argument
"Hello $message !"
}
}
----

If we generate HTML we get the following output:

If we set the options attribute with the value nowrap the long lines are no longer wrapped
and we get a horizontal scrollbar to see the complete code listing:

Miscellaneous

77

[source,groovy,options="nowrap"]
---class GroovyHelloWorld {
def sayHello(final String message = 'world') { // Set default argument value for message argument
"Hello $message !"
}
}
----

The following screenshot shows a horizontal scrollbar when we want to see the rest of the
line:

Written with Asciidoctor 0.1.4.


Original blog post written on June 11, 2014.

Creating a Checklist
Creating a list with Asciidoctor markup is easy. To create an unordered we need to start a
line with an asterisk (*) or hypen (-). We can add some extra markup to create a checked
list. If we add two square brackets ([]) to the list item we have a checklist. To have an
unchecked item we use a space, for a checked item we can use a lowercase x (x) or an
asterisk (*).
In the next example we define a shopping cart list with Asciidoctor markup:
== Checklist
.Shopping cart
* [x] Milk // Checked with x
* [ ] Sugar // Unchecked
* [*] Chocolate // Checked with *

When we create the HTML file we get the following output:

Miscellaneous

78

If we use font-based icons with the document attribute :icons: font the checkboxes are
rendered using fonts:

The checkboxes are now simply output in the HTML file. We can add an options attribute
to our list to make the checkboxes interactive:
== Checklist
// Make interactive checklist.
[options="interactive"]
.Shopping cart
* [x] Milk
* [ ] Sugar
* [*] Chocolate

If we transform this markup to HTML we see the following in our web browser:

Written with Asciidoctor 1.5.2.


Original blog post written on March 02, 2015.

Change Start Number for Numbered List


With Asciidoctor we can create numbered lists easily. When we want to change the number
the list starts with we use the start attribute when we define the list.

Miscellaneous

79

== Numbered List
.Default
. Makes writing easy
.. Keep focus
.. Syntax
. Different output formats
// Start this list from 10.
[start=10]
.Start from 10
. Makes writing easy
// We can use it on all levels.
[start=10]
.. Keep focus
.. Syntax
. Different output formats

We get the following HTML output when we transform the document:

Written with Asciidoctor 1.5.2.


Original blog post written on February 26, 2015.

Customize the Figure Captions


With Asciidoctor we can use images in our documents with the image directive. When the
document is converted each image gets a caption. By default the caption label is Figure
followed a number for the position of the image in the document. So the first image has a
caption Figure 1.. If we add a block title (text prefixed with a .) to the image then that text

Miscellaneous

80

is used in the caption as well. We can customize the caption label, figure counter, caption
text or disable the figure caption using a combination of document and image attributes.
We have the following Asciidoctor markup. We include several images and customize the
figure caption settings. To change the caption label (Figure) we set a different value for the
document attribute figure-caption. In our example we use the value Logo. Any captions
following this definition will have the label Logo.
To use a separate counter we can use a counter attribute inside the caption attribute for
our image. In our example we use this for the Gradle logo. If we want to use another caption
text instead of the block title we can use the title attribute for an image.
Finally to disable all figure captions we negate the figure-caption document attribute.
= Figure caption
// Default the figure caption
// label is Figure.
.SDKMAN!
image::./sdkman-logo.png[]
// Set caption for figures
// for the rest of the document
// to the value Logo.
:figure-caption: Logo
// The figure caption label is Logo.
.Groovy
image::./groovy-logo.png[]
// Custom caption label for this image
// where we can still use a counter.
.Gradle
image::./gradle-logo.png[caption="Logo 1-{counter:logo}"]
// Instead of using the image block
// title in the caption we define
// our own caption text with the title
// attribute.
.Ratpack
image::./ratpack-logo.png[title="Ratpack library"]
// Disable all captions for figures.
:!figure-caption:
.Grails
image::./grails-logo.png[]

When we transform this markup to HTML we get the following output:

Miscellaneous

81

Written with Asciidoctor 1.5.4.


Original blog post written on September 26, 2016.

Trick To Use Caption Labels And Numbers In References


In Asciidoctor we can add an anchor with an ID to a section or title and then reference it
in a link. The title of the section is used as link text. We can alter that when we define the

Miscellaneous

82

link, but if we rely on the default behaviour we create a title for our section including the
caption label and number. This way the created link points to the correct section and the
text contains the caption text and number for that section.
In the following example markup we can see how we can use the caption label and section
counter as attributes in the title. We do this with the title attribute of a section. By using
the single quotes we tell Asciidoctor to interpret the attributes. We must also make sure
we set the caption attribute to an empty string value. This disables the default caption
creation of Asciidoctor for our section. Finally we need to provide an ID for the section
using the #ID syntax:
= Code examples
// Enable the captions for listing blocks.
:listing-caption: Listing
== Creating an application
To create a simple Ratpack application we write
the following code:
// Our listing block has an id of SimpleJavaApp,
// so we can reference it as a link.
// The link text is the title of this listing block.
// We use the listing caption support of Asciidoctor
// in our title with the attributes listing-caption
// and counter:refnum. The value of listing-caption
// is defined with a document attribute (Listing)
// and counter:refnum contains the counter value
// for listing blocks.
// Finally we empty the caption attribute, otherwise
// the default caption rule is used to show Level {counter}.
[#SimpleJavaApp,source,java,caption='',title='{listing-caption} {counter:refnum}. Simple Java Rat\
pack application']
---package com.mrhaki;
import ratpack.server.RatpackServer;
public class Main {
public static void main(String... args) throws Exception {
RatpackServer.start(server ->
server
.handlers(chain ->
chain
.get(ctx -> ctx.render("Hello World!"))));
}
}
---// A second section also with an ID
// and custom caption and title attributes.
[#SimpleGroovyApp,source,groovy,caption='',title='{listing-caption} {counter:refnum}. Simple Groo\
vy Ratpack application']
---package com.mrhaki

Miscellaneous

import static ratpack.groovy.Groovy.ratpack


ratpack {
handlers {
get {
render "Hello World!"
}
}
}
---//
//
//
//
As
of

In these paragraphs we create a link to the sections with


id's SimpleJavaApp and SimpleGroovyApp. The text of the links
will be Listing 1. Simple Java Ratpack application and
Listing 2. Simple Groovy Ratpack application.
we can see in <<SimpleJavaApp>> the code is simple. The configuration
the Ratpack application is done using a series of methods.

With the Groovy code in <<SimpleGroovyApp>> we can use a DSL to define


the application. This results in even better readable code.

When we generate a HTML version of this markup we get the following result:

83

Miscellaneous

84

Written with Asciidoctor 1.5.4.


Original blog post written on September 26, 2016.

Changing Values for Default Captions


Asciidoctor has several captions and labels that can be overridden with document attributes. We need to define a document attribute and assign a new value to override a
default caption or label. We can use UTF-8 characters as the value. The following list shows
captions and labels we can override:
:appendix-caption:

Miscellaneous

85

:caution-caption:
:example-caption:
:figure-caption:
:important-caption:
:last-update-label:
:manname-title:
:note-caption:
:table-caption:
:tip-caption:
:toc-title:
:untitled-label:
:version-label:
:warning-caption:

In the next example Asciidoctor document we override caution-caption and last-updatelabel:


= Change default captions
:caution-caption: Watch out!
:last-update-label: I was created on
== Sample
CAUTION: Simple caution message to show changed caption.

We get the following HTML output:

This mechanism can be used to provide messages for other languages than English. The
Asciidoctor Github repository contains a file with already translated values for a lot of languages. We include this document in our own Asciidoctor markup and we set the document
attribute lang with the value we want, eg. nl. In the following example document we include the file https://raw.githubusercontent.com/asciidoctor/asciidoctor/v1.5.5/data/locale/attri

Miscellaneous

86

= Asciidoctor
// Set lang document attribute.
// This attribute is used in the
// included document attributes.adoc.
:lang: nl
// Include translations for built-in captions and labels.
// To make the inclusion with a URI work we must
// run Asciidoctor with attribute allow-uri-read:
// $ asciidoctor -a allow-uri-read sample.adoc
:i18n-labels-uri: https://raw.githubusercontent.com/asciidoctor/asciidoctor/v1.5.5/data/locale
include::{i18n-labels-uri}/attributes.adoc[]
// Simple caution block, where caption
// should be replaced by Dutch text.
CAUTION: Simpel bericht met `lang` document attribuut: {lang}.
// Labels for example blocks are also
// translated.
.Titel
====
Bijvoorbeeld label voor voorbeelden is ook aangepast.
====

When we invoke Asciidoctor via the command line we must add the option -a allow-uriread to the remote document is included. The following screenshot shows the output:

Written with Asciidoctor 1.5.5.


Original blog post written on October 11, 2016.

Display Keyboard Shortcuts


When we want to explain in our documentation which keys a user must press to get to
a function we can use the keyboard macro in Asciidoctor. The macro will output the key
nicely formatted as a real key on the keyboard. The syntax of the macro is kbd:[key]. To
get the desired output we must set the document attribute experimental otherwise the
macro is not used.

Miscellaneous

87

In the next Asciidoctor example file we use the keyboard macro:


= Keyboard macro
With the keyboard macro `kbd:[shortcut]`
we can include nicely formatted keyboard
shortcuts.
// We must enable experimental attribute.
:experimental:
// Define unicode for Apple Command key.
:commandkey: Yw84;
Press kbd:[{commandkey} + 1] or kbd:[Ctrl + 1]
to access the _Project_ view.
To zoom out press kbd:[Ctrl + -].
Find files with kbd:[Ctrl + Alt + N] or kbd:[{commandkey} + Shift + N].

When we transform this to HTML with the built-in HTML5 templates we get the following
output:

Written with Asciidoctor 1.5.2.


Original blog post written on April 22, 2015.

Using Asciidoctor In Javadoc Comments


Asciidoctor is a great tool for writing technical documentation. The documentation to our
Java source is what we write in Javadoc comments. Wouldnt it be nice if we could use
Asciidoctor in our Javadoc comments? Of course! We can achieve this with the Asciidoclet
Javadoc doclet. The doclet processes the Javadoc comments as Asciidoctor source and
generates HTML in the final Javadoc documentation. We can use all of Asciidoc syntax like
tables, lists, include directives, styling and more. We can even use Asciidoctor extensions
like asciidoctor-diagram.

Miscellaneous

88

In the following Java source code we have Javadoc comments with Asciidoctor syntax. We
have document attributes, list, styling, include macro, table and asciidoctor-diagram code
in our Javadoc. Notice that we dont have the clutter of HTML tags we normally we would
have if we write Javadoc.
package com.mrhaki.sample;
/**
* = Application
*
* Project version: {projectVersion}.
*
* Sample Java application in project {projectName}
* to show Asciidoclet as replacement for the
* default Javadoclet.
*
* We can apply Asciidoc syntax in our Javadoclet
* comments, like:
*
* - `code`
* - **bold**
* - _italics_
*
* include::./src/main/javadoc/usage.adoc[]
*
* [plantuml]
* ....
* hide footbox
*
* actor Client
* Client -> Application : main()
* ....
*
* @author mrhaki
* @version 1.0
*/
public class Application {
/**
* Main method to start the application.
*
* The following arguments are allowed
* (easy Asciidoc table syntax):
*
* |===
* | Name | Description
*
* | --help
* | Getting more help about the application.
*
* | --info
* | Show extra logging information
*
* |===
*

Miscellaneous

89

*/
public static void main(final String... arguments) {
System.out.println("Application started...");
}
}

Next we create a Gradle build file and configure the javadoc task to use the Asciidoclet
Javadoc doclet. In our Java class we also used the asciidoctor-diagram support and
therefore we also need to set up the Gradle jruby plugin.
plugins {
// jruby plugin needed for asciidoctor-diagram gem.
id 'com.github.jruby-gradle.base' version '0.1.5'
}
apply plugin: 'java'
version = '1.2.1'
ext {
asciidoctorDocletVersion = '1.5.2'
}
configurations {
// Extra configuration to assign Asciidoclet dependencies to.
// This way the dependency will not interfer with oterh configurations.
asciidoctorDoclet
}
repositories.jcenter()
dependencies {
// Define dependencies for Asciidoclet.
asciidoctorDoclet group: 'org.asciidoctor',
name: 'asciidoclet',
version: project.asciidoctorDocletVersion
// Define dependency on asciidoctor-diagram Ruby gem.
// Only needed when we want to use asciidoctor-diagram in our Javadoc.
gems 'rubygems:asciidoctor-diagram:1.2.0'
}
// Download required gems. Only when we need gems in our Javadoc.
javadoc.dependsOn jrubyPrepareGems
javadoc {
// Configure Javadoc options.
options.with {
// Configure Asciidoclet class path and class name
// for the Javadoc task.
docletpath = configurations.asciidoctorDoclet.files.asType(List)
doclet = 'org.asciidoctor.Asciidoclet'
// Set base dir. E.g. used for include directives.

Miscellaneous

90

// Asciidoclet attributes need to be prefixed with -.


// - is turned into -- when Javadoc tool is executed.
addStringOption '-base-dir', projectDir.toString()
// We can add Asciidoc document attributes and use them
// in our Javadoc comments. E.g. {projectName}.
def attributes = [projectName: project.name,
projectVersion: project.version]
// Configure Asciidoclet to use asciidoctor-diagram gem.
addStringOption '-require', 'asciidoctor-diagram'
addStringOption '-gem-path', jrubyPrepareGems.outputDir.absolutePath
// Include generated diagram inline.
attributes['data-uri'] = ''
// Combine document attributes with key/value pairs separated
// by a comma. These are the document attributes passed
// on to Asciidoctor.
addStringOption '-attribute', attributes*.toString().join(',')
// Overview document can also be a Asciidoctor document.
overview = 'src/main/javadoc/overview.adoc'
// Show version and author tags.
// Normal Javadoc tags are correctly processed.
version = true
author = true
}
}

When we run the javadoc task we get the generated output. For example part of the
generated documentation for our Application class looks like this:

Miscellaneous

Documentation for the main method:

91

Miscellaneous

92

Remember this only applies to Javadoc. If we write Groovy code and use Groovydoc to
generate documentation we cannot use Asciidoclet.
Written with Asciidoclet 1.5.2 and Gradle 2.7.
Original blog post written on October 12, 2015.

You might also like