Awesome Asciidoctor Notebook
Awesome Asciidoctor Notebook
Hubert A. Klein Ikkink (mrhaki)
Buy on Leanpub

Table of Contents

About Me

I am born in 1973 and live in Tilburg, the Netherlands, with my 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 Spring-based 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 isn’t 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.

I have spoken at the Gr8Conf Europe and US editions about Groovy, Gradle, Grails and Asciidoctor topics. Other conferences where I talked are Greach in Madrid, Spain, JavaLand in Germany and JFall in The Netherlands.

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.

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 Awesome Asciidoctor blog posts in a book and publish 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.

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:

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. Let's 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:

Written with Asciidoctor 1.5.1.

Original post written on November 7, 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.

Let's 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

| 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:

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 post written on November 6, 2014

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:

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. Let's 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:

Written with Asciidoctor 1.5.1.

Original post written on November 7, 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.

== 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

|===

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

Written with Asciidoctor 1.5.1.

Original post written on December 4, 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

|=== 


// 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 post written on December 2, 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.

Let's start with a very simple table. The last cell of the first row contains some Asciidoc markup:

: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 don't 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

// We specify for this specific cell the 
// contents is Asciidoc that needs to 
// be processed.
a| 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 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:

.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:

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:

Written with Asciidoctor 1.5.1.

Original post written on November 10, 2014

Nested Tables

Defining tables in Asciidoctor is very easy. The start and end of the table are defined by |===. But if we want to add a new table to a table cell we cannot use the same syntax. To define a nested table we must replace the | separator with !. So instead of |=== to indicate the table boundaries we use !===. Also the cell separators are now ! instead of |. Finally we must make sure the table cell or column supports Asciidoc markup, so the table is properly created. We must configure the cell or column with a so the nested table is created.

In the following example Asciidoctor markup we have a simple table with a nested table in the second column and row. Notice we can still apply all table configuration to the nested table as well:

= Tables

== Nested tables

To nest a table in a table we must
use `!` as table separator instead of `|`.
Also the type of the column or cell
must be set to `a` so Asciidoc markup
is processed.

[cols="1,2a"]
|===
| Col 1 | Col 2

| Cell 1.1
| Cell 1.2

| Cell 2.1
| Cell 2.2

[cols="2,1"]
!===
! Col1 ! Col2

! C11
! C12

!===

|===

When we run Asciidoctor to create HTML for this markup we get the following result:

Written with Asciidoctor 1.5.5.

Original post written on April 26, 2017

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:

Generated with Asciidoctor 0.1.4.

Original post written on June 4, 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 pipe-symbol (|) 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

== 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):

= 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 post written on November 5, 2014

Using Tab Separated Data In A Table

In a previous post we learned how to use data in CSV and DSV format. Recently we can also include tab separated values (TSV) in a Asciidoctor table. We must set the table attribute format to the value tsv. The data can be inside the document, but also defined in an external file which we add with the include macro.

In the following example markup we have a table with inline tab separated values. A second table includes an external file with tab delimited values:

= Tables

Using the `format` attribute value `tsv` we can
use tab-delimited data for table data.

== TSV table

[format=tsv, options="header"]
|===
Writing tools Awesomeness
Asciidoctor Oh yeah!
MS Word  No!
|===

== Table with external data

// We have an external file with 
// tab-delimited values.

[%header,format=tsv]
|===
include::tools.tsv[]
|===

When we convert our Asciidoctor markup to HTML we get the following result:

Written with Asciidoctor 1.5.6.1.

Original post written on October 5, 2017

Blocks

Using Filename Starting With Dot As Block Title

Adding a block title in Asciidoctor is easily done by adding a line at the top of the block that starts with a dot (.). The text following the dot is then used as the title of the block. But if the text of the title itself starts with a dot (.) Asciidoctor get's confused. For example if we want to use a filename that starts with a dot (.filename) we must use different syntax to set the block title with the filename.

In the next Ascciidoc markup sample we use different ways to set the block title for a code block with a filename that starts with a dot. First we use the title attribute for the block. Another solution is to use the Unicode value for the dot. Next we enclose the filename in back ticks (`) which also formats the filename with a monotype font. We can also separate the first dot with the dotted filename with the document attribute {blank}. Also we can define the dot as document attribute and use it in the title. And finally we can define the filename via a document attribute and reference the document attribute in the block title:

= Filenames starting with dot (.)

Several samples showing how to set block title when title starts with dot (`.`).

Code block title is filename that starts with dot (`.`), which confuses the parser:

[source,json]
// Title cannot be parsed correctly
// because of the 2 dots.
..eslintrc
----
{
    "key": "value"
}
----

Using explicit title attribute:

// Instead of using . notation
// for block title, we use the
// explicit block attribute
// title definition.
[source,json,title='.eslintrc']
----
{
    "key": "value"
}
----

Use Unicode value for dot:

[source,json]
// Use hexadecimal Unicode replacement for
// starting dot (.) in filename.
.&#x002E;eslintrc
// (Or with decimals: .&#46;eslintrc)
----
{
    "key": "value"
}
----

Format filename as code:

[source,json]
// Put filename between back ticks (`)
// and title is recognized again and
// nicely formatted with monotype font.
.`.eslintrc`
----
{
    "key": "value"
}
----

Using `\{blank}` document attribute to separate
title dot and filename:

[source,json]
// Separate title and filename with
// {blank} document attribute.
.{blank}.eslintrc
----
{
    "key": "value"
}
----

Using document attribute to define dot and use with filename:

[source,json]
// Section title is also parsed correctly
// if we use a document attribute
// to reflect dot.
:dot: .
.{dot}eslintrc
----
{
    "key": "value"
}
----

Using document attribute to set filename:

[source,json]
// Section title is also parsed correctly
// if we use a document attribute
// with the filename.
:snippetFilename: .eslintrc
.{snippetFilename}
----
{
    "key": "value"
}
----

[source,json]
// We can re-use the same document attribute
// for other code sections.
:snippetFilename: .jslintrc
.{snippetFilename}
----
{
    "key": "value"
}
----

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

Written with Asciidoctor 1.5.4

Original post written on January 2, 2017

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
====
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:

Written with Asciidoctor 1.5.2.

Original post written on December 11, 2014

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;

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:

To disable the listing captions we must negate the document attribute: :!listing-caption:.

Written with Asciidoctor 1.5.4.

Original post written on September 26, 2016

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:

[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 post written on June 11, 2014

Don't Wrap Lines in All Listing or Literal Blocks of Document

In a previous post we learned about setting the value of options attribute to nowrap on listing and literal blocks, so the lines in the block are not wrapped. In the comments a user suggested another option to disable line wrapping for all listing and literal blocks in the document by using the document attribute prewrap. We must negate the document attribute, :prewrap!:, to disable all wrapping. If we place this document attribute at the top of our Asciidoctor document it is applied for the whole document. We can also place it at other places in the document to change the setting for all listing and literal blocks following the prewrap document attribute. To enable wrapping again we set :prewrap: (leaving out the exclamation mark).

In the following example we have markup with a listing, literal and example block and we use the document attribute :prewrap!: to disable the wrapping for the listing and literal blocks:

= Wrapping lines sample

// Disable wrapping in listing and literal blocks.
:prewrap!:

== Listing

[.groovy]
----
class GroovyHelloWorld {
    String sayHello(final String message = 'world') { // Set default argument value for message argume\
nt
        "Hello $message !"
     }
}
----

== Literal

....
class GroovyHelloWorld {
    String sayHello(final String message = 'world') { // Set default argument value for message argume\
nt
        "Hello $message !"
     }
}
....

== Example

====
// We use spacing to mimic a code block.
class GroovyHelloWorld { +
{nbsp}{nbsp}{nbsp}{nbsp}String sayHello(final String message = 'world') { // Set default argument valu\
e for message argument +
{nbsp}{nbsp}{nbsp}{nbsp}{nbsp}{nbsp}{nbsp}{nbsp}"Hello $message !" +
{nbsp}{nbsp}{nbsp}{nbsp}} +
}
====

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

The code in the listing and literal blocks is now not wrapped, but continues on the same line.

Written with Asciidoctor 2.0.2.

Original post written on May 22, 2019

Include Asciidoc Markup With Listing or Literal Blocks Inside Listing or Literal Block

If we want to include Asciidoc markup as source language and show the markup without transforming it we can use a listing or literal block. For example we are using Asciidoc markup to write a document about Asciidoctor and want to include some Asciidoc markup examples. If the markup contains sections like a listing or literal block and it is enclosed in a listing or literal block, the tranformation goes wrong. Because the beginning of the included listing or literal block is seen as the ending of the enclosing listing or literal block. Let's see what goes wrong with an example where we have the following Asciidoc markup:

In the following example we see a listing block that can be defined in Asciidoc markup.

[source,asciidoc]
----
= Sample document

We can include listing blocks which are ideal for including source code.

.A listing block
----
public class Sample { }
----

.A literal block
....
public class Sample { }
....

Asciidoctor awesomeness!
----

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

We can use nested listing or literal blocks where we have to add an extra hyphen or dot to the nested block, but then the Asciidoc markup we want to show as an example is not correct anymore. It turns out we can also add an extra hyphen or dot to the enclosing listing or literal block to transform our markup correctly. So in our example we add an extra hyphen to the outer listing block:

In the following example we see a listing block that can be defined in Asciidoc markup.

[source,asciidoc]
// This time we have 5 hyphens.
-----
= Sample document

We can include listing blocks which are ideal for including source code.

.A listing block
----
public class Sample { }
----

.A literal block
....
public class Sample { }
....

Asciidoctor awesomeness!
-----

The transformed HTML looks like this:

If our sample markup we want to show only contains a listing block we could have enclosed it in a literal block to get the same result or sample markup of a literal block could be in a listing block.\ But in our example we have both a listing and literal block so we needed another solution to get the desired result.

Written with Asciidoctor 2.0.9.

Original post written on May 24, 2019

Collapsible Content

Since Asciidoctor 2.0.0 we can add the collapsible option to an example block. When the markup is generated to HTML we get a HTML details and summary section. The content of the example block is collapsed (default behaviour because it is in a details section) and a clickable text is available to open the collapsed block (the summary section), so we can see the actual content. The text we can click on is by default Details, but we can change that by setting the title of the example block. Then the title is used as the text to click on to open the collapsed content.

The following example markup has two collapsible blocks with and without a title:

= Sample
:nofooter:
:source-highlighter: highlightjs

== Collapse

[%collapsible]
====
Example block turns into collapsible summary/details.
====

== Exercise

. Implement the `Application` class with `main(String[] args)` method.

=== Solution

// The title attribute is used as
// clickable text to open the example block.
.Click to see solution
[%collapsible]
====
[,java]
----
package mrhaki;

import io.micronaut.runtime.Micronaut;

public class Application {

    public static void main(String[] args) {
        Micronaut.run(Application.class);
    }
}
----
====

When we generate this markup to HTML we get the following result:

And when we expand the collapsible content we see:

Written with Asciidoctor 2.0.2.

Original post written on March 28, 2019

Lists

Change Number Style For Ordered Lists

To write a (nested) ordered lists in Asciidoctor is easy. We need to start the line with a dot (.) followed by a space and the list item text. The number of dots reflects the levels of nesting. So with two dots (..) we have a nested list item. By default each nested level has a separate numbering style. The first level has arabic numbering, the second lower case alphanumeric, the third upper case alphanumeric, the fourth lower case roman and the fifth (which is maximum depth of nested levels in Asciidoctor) has style upper case roman. But we can change this by setting a block style for each nested level block. The name of the block style is arabic, loweralpha, upperalpha, lowerromann or upperroman. With the HTML5 backend we can also use decimal and lowergreek.

In the following example we have an ordered list where we set different block styles for the nested level:

= Nested numbering styles

. Services
[arabic]
.. Datastore
.. Mail

. Repositories
// Only HTML backend
[decimal]
.. PostgresDB
.. Redis

. Controllers
// Only HTML backend
[lowergreek]
.. API
.. Admin

. Software systems
// Or use upperalpha for upper case alphanumeric
[loweralpha]
.. OAuth

. Operatings systems
// Or use lowerroman for lower case roman
[upperroman]
.. Linux
[decimal]
... Server A
... Server B
.. macOS
.. Windows server

When we create the HTML output we have the following result:

Written with Asciidoctor 1.5.4.

Original post written on December 13, 2016

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:

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 post written on March 2, 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.

== 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 post written on February 26, 2015

Using Paragraphs in Lists With List Item Continuation

When we write a list in Asciidoctor we can simply create a list item by starting the line with a dot (.). To create a another list item we simply start a new line with a dot (.). But what if we want to add a list item with multiple paragraphs, or text and a source code block element. We can use the list item continuation (+) to indicate to Asciidoctor we want to keep these together for a single list item.

In the following example we have a list in Asciidoctor markup. The second list item has multiple paragraphs , the third item has an extra admonition block and the fourth item contains a source code block:

:icons: font

== List continuation

When we have a list item that has for example multiple paragraphs,
we can use Asciidoctor's list continuation feature. We place a
`+` symbol between the paragraphs to indicate the paragraphs
belong to a single list item.

=== Sample list

. A very simple first item
. This item consists of two paragraphs.
+
By adding the `+` symbol we indicate this
paragraph also belongs to the second list item.
. We can even add for example an admonition.
+
TIP: Did you know Asciidoctor is awesome?
. A small code example:
+
[source,groovy]
----
println 'Groovy rocks!'
----
. Let's end with a simple list item.

Let's generate this Asciidoctor markup to HTML and we see the following result:

We see how the extra paragraph, admonition and source code are part of a single list item.

Written with Asciidoctor 1.5.6.1.

Original post written on October 3, 2017

Use Only Block As List Item

When we define a list in Asciidoctor we usually have a list item that is a paragraph. But if we want to have a block as list item we need to add an extra {blank} element to make sure the block is parsed correctly as list item. Because a list item starts with a . or a * at the beginning of a line and a block also is defined on the beginning of the line, we must add the extra {blank} element. Together with the list item continuation (+) we can have a list with blocks.

In the following example we define a numbered list with three listing blocks:

= Simple steps

We need to type the following commands to generated a HTML version
of our Asciidoctor source document:

. {blank}
+
----
$ asciidoctor sample.adoc
$
----
. {blank}
+
----
$ ls
sample.adoc      sample.html
$
----
. {blank}
+
----
$ open sample.html
$
----

When we generate a HTML version we get the following result:

Written with Asciidoctor 1.5.6.1.

Original post written on October 16, 2017

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,groovy,indent=3]
include::indent.groovy[]

Here is the resulting HTML:

Code written with Asciidoctor 0.1.4.

Original 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!
----

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

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:

Written with Asciidoctor 1.5.0.

Original 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:

// 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 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:

@marcovermeulen @rfletcherew @mrhaki you can also use -1 in the include range, like this https://t.co/J4tohY4cp3

— Andres Almiray (@aalmiray) June 6, 2014

\

Let's 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 post written on August 5, 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 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+ methods.
<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 post written on May 5, 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.

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:

Written with Asciidoctor 1.5.4.

Original post written on October 5, 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 pygments, 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,sql]
----
CREATE TABLE USER (
    ID INT NOT NULL,
    USERNAME VARCHAR(40) NOT NULL
);
----

The following screenshots show the result of applying different themes:

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 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.

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:

<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. Let's see the result in a web browser:

Written with Asciidoctor 1.5.4.

Original post written on October 4, 2016

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.

Let's 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 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
}

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 post written on November 12, 2014

Use Diagram Block Macro To Include Diagrams

With the Asciidoctor diagram extension we can include diagrams that are written in plain text. For example PlantUML or Ditaa diagrams. The extension offers a block processor where we include the diagram definitions in our Asciidoctor document. But there is also a block macro processor. With the block macro processor we can refer to an external file. The file is processed and the resulting image is in our output document.

In the following example we see how to use the block macro for a Ditaa diagram:

= Diagram Block Macro

There is another way to use the diagram
extension instead of via a block definition. We
can use a block macro to refer to an external
file that has the diagram definition.

The name of the diagram extension is the
block macro name. For example for a Ditaa diagram
we use `ditaa::` and for PlantUML
we use `plantuml::`. This is followed
by a filename of the file that contains the
diagram source.

We can define attributes just like with the
block definition. The first positional attribute
define the filename. Or we use the attribute
name `target` to set the output filename. The second
positional attribute is the file format. Instead
we can use the attribute `format`.

In the next example we use a block macro
to include a Ditaa diagram definition:

// The first positional attribute is the
// file target name (or we use attribute target),
// the second positional attribute is the
// file format (or use attribute format).
// Other attributes can also be defined.
ditaa::sample.ditaa[ditaa-diagram, png, round-corners="true"]

Written with Asciidoctor {asciidoctor-version}.

The contents of sample.ditaa:

+-------------+
| Asciidoctor |-------+
|   diagram   |       |
+-------------+       | PNG out
    ^                 |
    | ditaa in        |
    |                 v
+--------+   +--------+----+    /---------------\
|        |---+ Asciidoctor +--->|               |
|  Text  |   +-------------+    |   Beautiful   |
|Document|   |   !magic!   |    |    Output     |
|     {d}|   |             |    |               |
+---+----+   +-------------+    \---------------/
    :                                   ^
    |          Lots of work             |
    +-----------------------------------+

When we create a HTML document from this Asciidoctor markup we get the following output:

Written with Asciidoctor 1.5.6.1.

Original post written on October 30, 2017

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 it's 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[])\
  • 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.

Let's 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}", attribute\
s, options)

        // 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.

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'
}

Let's 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.twit\
ter.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 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:

= 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 don't 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()
        }
 
    }    
}

Let's transform our sample Asciidoctor markup to HTML. We see the following result:

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

Written with Gradle 2.3 and Asciidoctor 1.5.2.

Original post written on March 4, 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:

= 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 post written on November 8, 2016

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 don't see any of the comments, not even in the HTML source:

Written with Asciidoctor 1.5.0.

Original 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 post written on June 4, 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:\

: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 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.html 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:

= 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:

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 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 (++++).

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</strong>].

The generated HTML looks like this:

Code written with Asciidoctor 0.1.4.

Original 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:

: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:

// 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 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/font-awesome/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 post written on August 15, 2014

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-awesome\
.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 post written on August 15, 2014

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 &#169; which is the copyright symbol: ©.

In the following sample we see all the symbol replacements:

Written with Asciidoctor 1.5.2.

Original 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.

Section icon is link reference.

With the sectlinks attribute the section title itself is the link. So we don't 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 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 don't 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.

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:

Written with Asciidoctor 1.5.2.

Original post written on September 10, 2015

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 shouldn't 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 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
= Second 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 post written on September 20, 2016

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 :<:start-value>. Let's 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.

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

\

Written with Asciidoctor 1.5.4.

Original post written on September 16, 2016

To make an image linkable in Asciidoctor when formatted to HTML we must use the link attribute when we use the image macro. The value of the link attribute is the address where the user goes when clicking on the image. We can also specify extra link attributes like window to specify the target window for the link to open in.

In the following example we use the link attribute for a block and inline image, with and without an extra window attribute:

= Image with link

== Simple link

Using the `link` attribute with `image:`

image::haki-logo.png[link="https://www.mrhaki.com"]
A block image.

image:haki-logo.png[link="https://www.mrhaki.com"]
As inline image.

== Link attributes

Set link attribute `window="_blank"` to open link in new browser window:

image::haki-logo.png[link="https://www.mrhaki.com",window="_blank"]

When we transform this markup to HTML we get the following HTML for the images:

...
<div class="paragraph">
<p>Using the <code>link</code> attribute with <code>image:</code></p>
</div>
<div class="imageblock">
<div class="content">
<a class="image" href="https://www.mrhaki.com"><img src="./images/haki-logo.png" alt="haki logo"></a>
</div>
</div>
<div class="paragraph">
<p>A block image.</p>
</div>
<div class="paragraph">
<p><span class="image"><a class="image" href="https://www.mrhaki.com"><img src="./images/haki-logo.png\
" alt="haki logo"></a></span>
As inline image.</p>
</div>
</div>
...
<div class="imageblock logo">
<div class="content">
<a class="image" href="https://www.mrhaki.com" target="_blank" rel="noopener"><img src="./images/haki-\
logo.png" alt="haki logo"></a>
</div>
...

Written with Asciidoctor 2.0.9.

Original post written on April 23, 2020

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:

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 post written on August 12, 2014

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:

<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 post written on June 13, 2014

Prevent Transformation of URL to Hyperlink

Normally if we type an URL in Asciidoctor that starts with a scheme Asciidoctor knows about, the URL is turned into a hyperlink. The following schemes are recognized by Asciidoctor:

  • http
  • https
  • ftp
  • irc
  • mailto
  • email@email.com

If we want to keep our URL as text and not a link we must prepend our URL with a backslash (\). This way Asciidoctor will not transform the URL to a hyperlink in our output.

In the following example we have URL that is transformed to a link, followed by the same URL but with a backslash (\) before it, that is not transformed:

== URL not as a link

The URL http://www.asciidoctor.org  should
be turned into a hyperlink.

But now the URL \http://www.asciidoctor.org should
just be text and not a hyperlink.

If we transform our document to HTML with Asciidoctor we get the following result:

Written with Asciidoctor 1.5.6.1.

Original post written on October 12, 2017

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:

Written with Asciidoctor 1.5.2.

Original 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 and use the value attributes+ to instruct Asciidoctor to replace attributes with their values:

: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 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):

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 post written on July 3, 2014

Document Attributes With Styling

Document attributes in Asciidoctor are very powerful. We can assign values to a document attributes and reference the attribute name in our document enclosed between curly braces. Asciidoctor will fill in the value when the document is transformed. Instead of a plain value we can also use styling markup in the document attribute definition. We must use the passthrough macro and allow for quote substitution.

In the following example document we define three document attributes: cl-added, cl-updated and cl-changed. We use the passthrough macro, quotes substation to assign CSS classes:

= Attributes with styles
// Include contents of docinfo.html
// in HTML head with CSS style
// definitions for .label.added,
// .label.changed and .label.updated
// used in the document attributes
// cl-added, cl-changed and cl-updated.
:docinfo1:

// Document attribues with styling,
// using the passthrough macro
// and quotes subsitution.
// We can use quotes or the short-hand q.
:cl-added: pass:quotes[[.label.added]#Added:#]
:cl-changed: pass:q[[.label.changed]#Changed:#]
:cl-updated: pass:q[[.label.updated]#Updated:#]


== Sample section

* {cl-added} Document attributes for document.
* {cl-changed} Definition of attributes to include
 more options.
* {cl-updated} New version of Asciidoctor.

Notice we need a file docinfo.html with the CSS style definitions:

<style>
.label {
    color: #fff;
    padding: .2em .6em .3em;
    font-weight: 700;
    border-radius: .25em;
    font-size: 90%;
}

.added {background-color: #007700;}
.changed {background-color: #088;}
.updated {background-color: #3344bb;}
</style>

When run Asciidoctor to get HTML output we see the following:

Written with Aciidoctor 1.5.7.1.

Original post written on September 2, 2018

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:

|===
| 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:

\

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:

|===
| Attributes

|
:version: {version}

|===


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


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

Let's generate HTML with different values for the version attribute:

Written with Asciidoctor 0.1.4.

Original post written on August 5, 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:

: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 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.

Let's 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

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 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:

// 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 post written on June 25, 2014

Auto Number Callouts

In a previous post we learned about callouts in Asciidoctor to add explanation to source code. While surfing the Internet I came upon the following blog post by Alex Soto: Auto-numbered Callouts in Asciidoctor. I turns out that since Asciidoctor 1.5.8 we can use a dot (.) instead of explicit numbers to have automatic increasing numbering for the callouts.

Let's take our example from the earlier blog post and now use auto numbered callouts:

= Callout sample
:source-highlighter: prettify
:icons: font

[source,groovy]
----
package com.mrhaki.adoc

class Sample {
    String username // <.>

    String toString() {
        "${username?.toUpperCase() ?: 'not-defined'}" // <.>
    }
}
----
<.> Simple property definition where Groovy will generate the `setUsername` and `getUsername` methods.
<.> Return username in upper case if set, otherwise return `not-defined`.

When we convert this markup to HTML we get the following result:

This makes it very easy to add new callouts without having to change all numbers we first typed by hand.

Written with Asciidoctor 2.0.9

Original post written on December 10, 2019

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 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
$
----

// 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 post written on June 16, 2014

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 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:

\

Written with Asciidoctor 1.5.4.

Original post written on September 26, 2016

Grouping Floating Images

With Asciidoctor markup we can position images in our document. We can even float images, so text can next to an image, inside only below or about the image. We can also define multiple images to float, so the images are displayed on the same line next to each other. Any text that follows these images is also displayed next to the images. If we want only to have floating images, but the text starting under the images we can place the images inside an open block and assign the block the role float-group.

In the next example we first define three images that all have roles to float left. In the second part we group these images using the role float-group, so the text will not be displayed next to the images, but under the images:

:imagesdir: images/

= Grouping floats

// Images in open block to indicate 
// they belong together.
--
image::groovy.png[float="left"]
image::gradle.png[float="left"]
// Define float role, instead of attribute.
[.left]
image::grails.png[]
--

The images are all on one line,
but the text is next to the images.

If we want the images to be on one line,
but the text underneath them, we must use
a _float-group_ role, like in the
following example.

[.float-group]
--
image::groovy.png[float="left"]
image::gradle.png[float="left"]
// Define float role, instead of attribute.
[.left]
image::grails.png[]
--

The images above are all grouped together
to appear on one line.

And the text is not next to the images, but
underneath the images, like we wanted.

When we create a HTML document from the Asciidoctor markup we get the following result:

Written with Asciidoctor 1.5.6.1.

Original post written on October 4, 2017

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 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 Ratpack \
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 Groovy Ra\
tpack application']
----
package com.mrhaki

import static ratpack.groovy.Groovy.ratpack

ratpack {
    handlers {
        get {
            render "Hello World!"
        }
    }
}
----

// 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.
As we can see in <<SimpleJavaApp>> the code is simple. The configuration
of 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:

Written with Asciidoctor 1.5.4.

Original 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:
  • :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-update-label:

= 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/attributes.adoc.

= 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-uri-read to the remote document is included. The following screenshot shows the output:

Written with Asciidoctor 1.5.5.

Original 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.

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: &#8984;

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 post written on April 22, 2015

Exclude Parts From Included Files

In a previous post we learned how to include parts of a document in the generated output. The included parts are defined using tags. The start of a tag is defined in a comment with the format tag::tagName[] and the end has the format end::tagName[]. Next we must use the tags attribute for the include macro followed by the tagName. If we don't want to include a tag we must prefix it with an exclamation mark (!).

Suppose we have an external Java source we want to include in our Asciidoctor document.

package mrhaki;

// tag::singletonAnnotation[]
@Singleton
// end::singletonAnnotation[]
public class Sample {
    public String greeting() {
        return "Hello Asciidoctor";
    }
}

In the following sample Asciidoctor document we include Sample.java, but we don't want to include the text enclosed with the singletonAnnotation tag. So we use tags=!singletonAnnotaion with the include macro:

= Sample

To NOT include sections enclosed with tags we must use `tags=!<tagName>` in the `include` directive.

[source,java]
----
include::Sample.java[tags=!singletonAnnotation]
----

When we transform our Asciidoctor markup to HTML we get the following result:

Written with Asciidoctor 1.5.6.1.

Original post written on January 29, 2019

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. Wouldn't 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.

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 don't 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
     *
     * |===
     *
     */
    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.
        // 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:

Documentation for the main method:

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 post written on October 12, 2015