Includes

You can include content from another file into the current AsciiDoc document using the include directive. The included content can be AsciiDoc or it can be any other text format. Where that content is included in the document determines how it will be processed.

What is an include directive?

An include directive imports content from an external file into the content of the current document. When the current document is processed, the include directive syntax is replaced by the contents of the include file. Think of the include directive like a file expander. The include directive is a preprocessor directive, so it has no awareness of the surrounding context.

When is an include directive useful?

The include directive is useful when you want to:

  • Partition a large document into smaller files for better organization and to make restructuring simpler.

  • Insert source code from the external files where the code is maintained.

  • Populate tables with output, such as CSV data, from other programs.

  • Create document variants by combining the include directive with conditional preprocessor directives.

  • Reuse content snippets and boilerplate content, such as term definitions, disclaimers, etc., multiple times within the same document.

Include directive syntax

An include directive must be placed on a line by itself with the following syntax:

include::path[leveloffset=offset,lines=ranges,tag(s)=name(s),indent=depth,opts=optional]

The leveloffset, lines, tag(s), indent, and opts attributes are optional, making the simplest case look like:

include::content.adoc[]

Include processing

Although the include directive looks like a block macro, it’s not a macro and therefore isn’t processed like one. It’s a preprocessor directive; it’s important to understand the distinction.

A preprocessor directive is processed when the lines of a document are read, but before the document structure is parsed. Therefore, it’s not aware of the surrounding document structure. A preprocessor directive merely adds lines to the reader or takes lines away. The include directive is a preprocessor directive that always adds lines.

The best way to think of the include directive is to imagine that it is being replaced by the lines from the include file (i.e., the imported lines). Only after the lines from the target of the include directive are added to the current document does the parser read and interpret those lines.

The include directive is disabled when Asciidoctor is run in secure mode. In secure mode, the include directive is converted to a link in the output document. See Safe Modes to learn more.

Escaping an include directive

If you don’t want the include directive to be processed, you must escape it using a backslash.

\include::just-an-example.ext[]

Escaping the directive is necessary even if it appears in a verbatim block since it’s not aware of the surrounding document structure.

Include file resolution

The path used in an include directive can be relative or absolute.

If the path is relative, the processor resolves the path using the following rules:

  • If the include directive is used in the primary (top-level) document, relative paths are resolved relative to the base directory. (The base directory defaults to the directory of the primary document and can be overridden from the CLI or API).

  • If the include directive is used in a file that has itself been included, the path is resolved relative to the including (i.e., current) file.

These defaults make it easy to reason about how the path to the include file is resolved.

If the processor cannot locate the file (perhaps because you mistyped the path), you’ll still be able to convert the document. However, you’ll get the following warning message during conversion:

asciidoctor: WARNING: my-document.adoc: line 3: include file not found: /.../content.adoc

The following message will also be inserted into the output:

Unresolved directive in my-document.adoc - include::content.adoc[]

To fix the problem, edit the file path and run the converter again. If you don’t want Asciidoctor to trigger a warning, and instead drop the include that cannot be found, add the opts=optional attribute to the include directive.

If you store your AsciiDoc files in nested folders at different levels, relative file paths can quickly become awkward and inflexible. A common pattern to help here is to define the paths in attributes defined in the header, then prefix all include paths with a reference to one of these attributes:

:includedir: _includes
:sourcedir: ../src/main/java

include::{includedir}/fragment1.adoc[]

[source,java]
----
include::{sourcedir}/org/asciidoctor/Asciidoctor.java[]
----

Keep in mind that no matter how Asciidoctor resolves the path to the file, access to that file is limited by the safe mode setting under which Asciidoctor is run. If a path violates the security restrictions, it may be truncated.

AsciiDoc vs non-AsciiDoc files

The include directive performs a simple file merge, so it works with any text file. The content of all included content is normalized. This means that the encoding is forced to UTF-8 (or converted from UTF-16 to UTF-8 if the file contains a BOM) and trailing whitespace and endlines are removed from each line and replaced with a Unix line feed. This normalization is important to how Asciidoctor works.

If the file is recognized as an AsciiDoc file (i.e., it has one of the following extensions: .asciidoc, .adoc, .ad, .asc, or .txt), Asciidoctor runs the preprocessor on the lines, looking for and interpreting the following directives:

  • includes

  • preprocessor conditionals (e.g., ifdef)

This allows includes to be nested, and provides lot of flexibility in constructing radically different documents with a single primary document and a few command line attributes.

Including non-AsciiDoc files is normally done to merge output from other programs or populate table data:

.2016 Sales Results
,===
include::sales/2016/results.csv[]
,===

In this case, the include directive does not do any processing of AsciiDoc directives. The content is inserted as is (after being normalized).