Module: Asciidoctor

Defined in:
lib/asciidoctor.rb,
lib/asciidoctor/list.rb,
lib/asciidoctor/block.rb,
lib/asciidoctor/table.rb,
lib/asciidoctor/inline.rb,
lib/asciidoctor/parser.rb,
lib/asciidoctor/reader.rb,
lib/asciidoctor/helpers.rb,
lib/asciidoctor/logging.rb,
lib/asciidoctor/section.rb,
lib/asciidoctor/timings.rb,
lib/asciidoctor/version.rb,
lib/asciidoctor/callouts.rb,
lib/asciidoctor/document.rb,
lib/asciidoctor/converter.rb,
lib/asciidoctor/extensions.rb,
lib/asciidoctor/cli/invoker.rb,
lib/asciidoctor/cli/options.rb,
lib/asciidoctor/stylesheets.rb,
lib/asciidoctor/substitutors.rb,
lib/asciidoctor/abstract_node.rb,
lib/asciidoctor/path_resolver.rb,
lib/asciidoctor/abstract_block.rb,
lib/asciidoctor/attribute_list.rb,
lib/asciidoctor/converter/base.rb,
lib/asciidoctor/converter/html5.rb,
lib/asciidoctor/converter/factory.rb,
lib/asciidoctor/converter/manpage.rb,
lib/asciidoctor/converter/docbook5.rb,
lib/asciidoctor/converter/template.rb,
lib/asciidoctor/converter/composite.rb,
lib/asciidoctor/converter/docbook45.rb

Overview

encoding: UTF-8

Defined Under Namespace

Modules: Cli, Compliance, Converter, Extensions, Helpers, LoggerManager, Logging, SafeMode, Substitutors, VoidWriter, Writer Classes: AbstractBlock, AbstractNode, AttributeList, Block, Callouts, Document, Inline, List, ListItem, Logger, MemoryLogger, NullLogger, Parser, PathResolver, PreprocessorReader, Reader, Section, Stylesheets, Table, Timings

Constant Summary collapse

RUBY_ENGINE =

alias the RUBY_ENGINE constant inside the Asciidoctor namespace

::RUBY_ENGINE
ROOT_PATH =

The absolute root path of the Asciidoctor RubyGem

::File.dirname ::File.dirname ::File.expand_path __FILE__
DATA_PATH =

The absolute data path of the Asciidoctor RubyGem

::File.join ROOT_PATH, 'data'
COERCE_ENCODING =

Flag to indicate whether encoding can be coerced to UTF-8 All input data must be force encoded to UTF-8 if Encoding.default_external is not UTF-8 Addresses failures performing string operations that are reported as “invalid byte sequence in US-ASCII” Ruby 1.8 doesn't seem to experience this problem (perhaps because it isn't validating the encodings)

!::RUBY_ENGINE_OPAL && ::RUBY_MIN_VERSION_1_9
FORCE_ENCODING =

Flag to indicate whether encoding of external strings needs to be forced to UTF-8

COERCE_ENCODING && ::Encoding.default_external != ::Encoding::UTF_8
BOM_BYTES_UTF_8 =

Byte arrays for UTF-* Byte Order Marks

[0xef, 0xbb, 0xbf]
BOM_BYTES_UTF_16LE =
[0xff, 0xfe]
BOM_BYTES_UTF_16BE =
[0xfe, 0xff]
FORCE_UNICODE_LINE_LENGTH =

Flag to indicate that line length should be calculated using a unicode mode hint

!::RUBY_MIN_VERSION_1_9
LF =

The endline character used for output; stored in constant table as an optimization

EOL = "\n"
NULL =

The null character to use for splitting attribute values

"\0"
TAB =

String for matching tab character

"\t"
MAX_INT =

Maximum integer value for “boundless” operations; equal to MAX_SAFE_INTEGER in JavaScript

9007199254740991
DEFAULT_DOCTYPE =

The default document type Can influence markup generated by the converters

'article'
DEFAULT_BACKEND =

The backend determines the format of the converted output, default to html5

'html5'
DEFAULT_STYLESHEET_KEYS =
['', 'DEFAULT'].to_set
DEFAULT_STYLESHEET_NAME =
'asciidoctor.css'
BACKEND_ALIASES =

Pointers to the preferred version for a given backend.

{
  'html'    => 'html5',
  'docbook' => 'docbook5'
}
DEFAULT_PAGE_WIDTHS =

Default page widths for calculating absolute widths

{
  'docbook' => 425
}
DEFAULT_EXTENSIONS =

Default extensions for the respective base backends

{
  'html' => '.html',
  'docbook' => '.xml',
  'pdf' => '.pdf',
  'epub' => '.epub',
  'manpage' => '.man',
  'asciidoc' => '.adoc'
}
ASCIIDOC_EXTENSIONS =

Set of file extensions recognized as AsciiDoc documents (stored as a truth hash)

{
  '.adoc' => true,
  '.asciidoc' => true,
  '.asc' => true,
  '.ad' => true,
  # TODO .txt should be deprecated
  '.txt' => true
}
SETEXT_SECTION_LEVELS =
{
  '=' => 0,
  '-' => 1,
  '~' => 2,
  '^' => 3,
  '+' => 4
}
ADMONITION_STYLES =
['NOTE', 'TIP', 'IMPORTANT', 'WARNING', 'CAUTION'].to_set
ADMONITION_STYLE_HEADS =
['N', 'T', 'I', 'W', 'C'].to_set
CALLOUT_LIST_HEADS =
['<', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'].to_set
PARAGRAPH_STYLES =
['comment', 'example', 'literal', 'listing', 'normal', 'open', 'pass', 'quote', 'sidebar', 'source', 'verse', 'abstract', 'partintro'].to_set
VERBATIM_STYLES =
['literal', 'listing', 'source', 'verse'].to_set
DELIMITED_BLOCKS =
{
  '--'   => [:open, ['comment', 'example', 'literal', 'listing', 'pass', 'quote', 'sidebar', 'source', 'verse', 'admonition', 'abstract', 'partintro'].to_set],
  '----' => [:listing, ['literal', 'source'].to_set],
  '....' => [:literal, ['listing', 'source'].to_set],
  '====' => [:example, ['admonition'].to_set],
  '****' => [:sidebar, ::Set.new],
  '____' => [:quote, ['verse'].to_set],
  '""'   => [:quote, ['verse'].to_set],
  '++++' => [:pass, ['stem', 'latexmath', 'asciimath'].to_set],
  '|===' => [:table, ::Set.new],
  ',===' => [:table, ::Set.new],
  ':===' => [:table, ::Set.new],
  '!===' => [:table, ::Set.new],
  '////' => [:comment, ::Set.new],
  '```'  => [:fenced_code, ::Set.new]
}
DELIMITED_BLOCK_HEADS =
DELIMITED_BLOCKS.keys.map {|key| key.slice 0, 2 }.to_set
LAYOUT_BREAK_CHARS =
{
  '\'' => :thematic_break,
  '<'  => :page_break
}
MARKDOWN_THEMATIC_BREAK_CHARS =
{
  '-'  => :thematic_break,
  '*'  => :thematic_break,
  '_'  => :thematic_break
}
HYBRID_LAYOUT_BREAK_CHARS =
LAYOUT_BREAK_CHARS.merge MARKDOWN_THEMATIC_BREAK_CHARS
NESTABLE_LIST_CONTEXTS =

LIST_CONTEXTS = [:ulist, :olist, :dlist, :colist]

[:ulist, :olist, :dlist]
ORDERED_LIST_STYLES =

TODO validate use of explicit style name above ordered list (this list is for selecting an implicit style)

[:arabic, :loweralpha, :lowerroman, :upperalpha, :upperroman]
ORDERED_LIST_KEYWORDS =
{
  #'arabic'     => '1',
  #'decimal'    => '1',
  'loweralpha' => 'a',
  'lowerroman' => 'i',
  #'lowergreek' => 'a',
  'upperalpha' => 'A',
  'upperroman' => 'I'
}
ATTR_REF_HEAD =
'{'
LIST_CONTINUATION =
'+'
HARD_LINE_BREAK =

NOTE AsciiDoc Python allows + to be preceded by TAB; Asciidoctor does not

' +'
LINE_CONTINUATION =
' \\'
LINE_CONTINUATION_LEGACY =
' +'
MATHJAX_VERSION =
'2.7.4'
BLOCK_MATH_DELIMITERS =
{
  :asciimath => ['\$', '\$'],
  :latexmath => ['\[', '\]'],
}
INLINE_MATH_DELIMITERS =
{
  :asciimath => ['\$', '\$'],
  :latexmath => ['\(', '\)'],
}
FONT_AWESOME_VERSION =
'4.7.0'
FLEXIBLE_ATTRIBUTES =

attributes which be changed within the content of the document (but not header) because it has semantic meaning; ex. sectnums

['sectnums']
AuthorInfoLineRx =

Matches the author info line immediately following the document title.

Examples

Doc Writer <doc@example.com>
Mary_Sue Brontë
/^(#{CG_WORD}[#{CC_WORD}\-'.]*)(?: +(#{CG_WORD}[#{CC_WORD}\-'.]*))?(?: +(#{CG_WORD}[#{CC_WORD}\-'.]*))?(?: +<([^>]+)>)?$/
RevisionInfoLineRx =

Matches the revision info line, which appears immediately following the author info line beneath the document title.

Examples

v1.0
2013-01-01
v1.0, 2013-01-01: Ring in the new year release
1.0, Jan 01, 2013
/^(?:\D*(.*?),)? *(?!:)(.*?)(?: *(?!^),?: *(.*))?$/
ManpageTitleVolnumRx =

Matches the title and volnum in the manpage doctype.

Examples

= asciidoctor(1)
= asciidoctor ( 1 )
/^(.+?) *\( *(.+?) *\)$/
ManpageNamePurposeRx =

Matches the name and purpose in the manpage doctype.

Examples

asciidoctor - converts AsciiDoc source files to HTML, DocBook and other formats
/^(.+?) +- +(.+)$/
ConditionalDirectiveRx =

Matches a conditional preprocessor directive (e.g., ifdef, ifndef, ifeval and endif).

Examples

ifdef::basebackend-html[]
ifndef::theme[]
ifeval::["{asciidoctor-version}" >= "0.1.0"]
ifdef::asciidoctor[Asciidoctor!]
endif::theme[]
endif::basebackend-html[]
endif::[]
/^(\\)?(ifdef|ifndef|ifeval|endif)::(\S*?(?:([,+])\S*?)?)\[(.+)?\]$/
EvalExpressionRx =

Matches a restricted (read as safe) eval expression.

Examples

"{asciidoctor-version}" >= "0.1.0"
/^(.+?) *([=!><]=|[><]) *(.+)$/
IncludeDirectiveRx =

Matches an include preprocessor directive.

Examples

include::chapter1.ad[]
include::example.txt[lines=1;2;5..10]
/^(\\)?include::([^\[][^\[]*)\[(.+)?\]$/
TagDirectiveRx =

Matches a trailing tag directive in an include file.

Examples

// tag::try-catch[]
try {
  someMethod();
catch (Exception e) {
  log(e);
}
// end::try-catch[]
/\b(?:tag|(e)nd)::(\S+?)\[\][\n \r]/
AttributeEntryRx =

Matches a document attribute entry.

Examples

:foo: bar
:First Name: Dan
:sectnums!:
:!toc:
:long-entry: Attribute value lines ending in ' \' \
             are joined together as a single value, \
             collapsing the line breaks and indentation to \
             a single space.
/^:(!?#{CG_WORD}[^:]*):(?:[ \t]+(.*))?$/
InvalidAttributeNameCharsRx =

Matches invalid characters in an attribute name.

/[^#{CC_WORD}-]/
AttributeEntryPassMacroRx =

In JavaScript, ^ and $ match the boundaries of the string when the m flag is not set

/\Apass:([a-z]+(?:,[a-z]+)*)?\[(.*)\]\Z/m
AttributeReferenceRx =

Matches an inline attribute reference.

Examples

{foobar} or {app_name} or {product-version}
{counter:sequence-name:1}
{set:foo:bar}
{set:name!}
/(\\)?\{(#{CG_WORD}+[-#{CC_WORD}]*|(set|counter2?):.+?)(\\)?\}/
BlockAnchorRx =

Matches an anchor (i.e., id + optional reference text) on a line above a block.

Examples

[[idname]]
[[idname,Reference Text]]
/^\[\[(?:|([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)(?:, *(.+))?)\]\]$/
BlockAttributeListRx =

Matches an attribute list above a block element.

Examples

# strictly positional
[quote, Adam Smith, Wealth of Nations]

# name/value pairs
[NOTE, caption="Good to know"]

# as attribute reference
[{lead}]
/^\[(|[#{CC_WORD}.#%{,"'].*)\]$/
BlockAttributeLineRx =

A combined pattern that matches either a block anchor or a block attribute list.

TODO this one gets hit a lot, should be optimized as much as possible

/^\[(?:|[#{CC_WORD}.#%{,"'].*|\[(?:|[#{CC_ALPHA}_:][#{CC_WORD}:.-]*(?:, *.+)?)\])\]$/
BlockTitleRx =

Matches a title above a block.

Examples

.Title goes here
/^\.(\.?[^ \t.].*)$/
AdmonitionParagraphRx =

Matches an admonition label at the start of a paragraph.

Examples

NOTE: Just a little note.
TIP: Don't forget!
/^(#{ADMONITION_STYLES.to_a * '|'}):[ \t]+/
LiteralParagraphRx =

Matches a literal paragraph, which is a line of text preceded by at least one space.

Examples

<SPACE>Foo
<TAB>Foo
/^([ \t]+.*)$/
AtxSectionTitleRx =

Matches an Atx (single-line) section title.

Examples

== Foo
// ^ a level 1 (h2) section title

== Foo ==
// ^ also a level 1 (h2) section title
/^(=={0,5})[ \t]+(.+?)(?:[ \t]+\1)?$/
ExtAtxSectionTitleRx =

Matches an extended Atx section title that includes support for the Markdown variant.

/^(=={0,5}|#\#{0,5})[ \t]+(.+?)(?:[ \t]+\1)?$/
SetextSectionTitleRx =

Matches the title only (first line) of an Setext (two-line) section title. The title cannot begin with a dot and must have at least one alphanumeric character.

/^((?=.*#{CG_WORD}+.*)[^.].*?)$/
InlineSectionAnchorRx =

Matches an anchor (i.e., id + optional reference text) inside a section title.

Examples

Section Title [[idname]]
Section Title [[idname,Reference Text]]
/ (\\)?\[\[([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)(?:, *(.+))?\]\]$/
InvalidSectionIdCharsRx =

Matches invalid ID characters in a section title.

NOTE uppercase chars not included since expression is only run on a lowercase string

/<[^>]+>|&(?:[a-z][a-z]+\d{0,2}|#\d\d\d{0,4}|#x[\da-f][\da-f][\da-f]{0,3});|[^ #{CC_WORD}\-.]+?/
AnyListRx =

Detects the start of any list item.

NOTE we only have to check as far as the blank character because we know it means non-whitespace follows.

/^(?:[ \t]*(?:-|\*\*{0,4}|\.\.{0,4}|\u2022\u2022{0,4}|\d+\.|[a-zA-Z]\.|[IVXivx]+\))[ \t]|[ \t]*.*?(?::::{0,2}|;;)(?:$|[ \t])|<?\d+>[ \t])/
UnorderedListRx =

Matches an unordered list item (one level for hyphens, up to 5 levels for asterisks).

Examples

* Foo
- Foo

NOTE we know trailing (.*) will match at least one character because we strip trailing spaces

/^[ \t]*(-|\*\*{0,4}|\u2022\u2022{0,4})[ \t]+(.*)$/
OrderedListRx =

Matches an ordered list item (explicit numbering or up to 5 consecutive dots).

Examples

. Foo
.. Foo
1. Foo (arabic, default)
a. Foo (loweralpha)
A. Foo (upperalpha)
i. Foo (lowerroman)
I. Foo (upperroman)

NOTE leading space match is not always necessary, but is used for list reader NOTE we know trailing (.*) will match at least one character because we strip trailing spaces

/^[ \t]*(\.\.{0,4}|\d+\.|[a-zA-Z]\.|[IVXivx]+\))[ \t]+(.*)$/
OrderedListMarkerRxMap =

Matches the ordinals for each type of ordered list.

{
  :arabic => /\d+\./,
  :loweralpha => /[a-z]\./,
  :lowerroman => /[ivx]+\)/,
  :upperalpha => /[A-Z]\./,
  :upperroman => /[IVX]+\)/
  #:lowergreek => /[a-z]\]/
}
DescriptionListRx =

Matches a description list entry.

Examples

foo::
bar:::
baz::::
blah;;

# the term may be followed by a description on the same line...

foo:: The metasyntactic variable that commonly accompanies 'bar' (see also, <<bar>>).

# ...or on a separate line, which may optionally be indented

foo::
  The metasyntactic variable that commonly accompanies 'bar' (see also, <<bar>>).

# attribute references may be used in both the term and the description

{foo-term}:: {foo-desc}

NOTE we know trailing (.*) will match at least one character because we strip trailing spaces NOTE negative match for comment line is intentional since that isn't handled when looking for next list item TODO check for line comment when scanning lines instead of in regex

%r(^(?!//)[ \t]*(.*?)(:::{0,2}|;;)(?:$|[ \t]+(.*)$))
DescriptionListSiblingRx =

Matches a sibling description list item (which does not include the type in the key).

{
  '::' => %r(^(?!//)[ \t]*(.*[^:]|)(::)(?:$|[ \t]+(.*)$)),
  ':::' => %r(^(?!//)[ \t]*(.*[^:]|)(:::)(?:$|[ \t]+(.*)$)),
  '::::' => %r(^(?!//)[ \t]*(.*[^:]|)(::::)(?:$|[ \t]+(.*)$)),
  ';;' => %r(^(?!//)[ \t]*(.*?)(;;)(?:$|[ \t]+(.*)$))
}
CalloutListRx =

Matches a callout list item.

Examples

<1> Foo

NOTE we know trailing (.*) will match at least one character because we strip trailing spaces

/^<?(\d+)>[ \t]+(.*)$/
CalloutListSniffRx =

Detects a potential callout list item.

/^<?\d+>/
CalloutExtractRx =

Matches a callout reference inside literal text.

Examples

<1> (optionally prefixed by //, #, -- or ;; line comment chars)
<1> <2> (multiple callouts on one line)
<!--1--> (for XML-based languages)

NOTE extract regexps are applied line-by-line, so we can use $ as end-of-line char

%r((?:(?://|#|--|;;) ?)?(\\)?<!?(|--)(\d+)\2>(?=(?: ?\\?<!?\2\d+\2>)*$))
CalloutExtractRxt =
'(\\\\)?<()(\\d+)>(?=(?: ?\\\\?<\\d+>)*$)'
CalloutScanRx =

NOTE special characters have not been replaced when scanning

/\\?<!?(|--)(\d+)\1>(?=(?: ?\\?<!?\1\d+\1>)*#{CC_EOL})/
CalloutSourceRx =

NOTE special characters have already been replaced when converting to an SGML format

%r((?:(?://|#|--|;;) ?)?(\\)?&lt;!?(|--)(\d+)\2&gt;(?=(?: ?\\?&lt;!?\2\d+\2&gt;)*#{CC_EOL}))
CalloutSourceRxt =
"(\\\\)?&lt;()(\\d+)&gt;(?=(?: ?\\\\?&lt;\\d+&gt;)*#{CC_EOL})"
ListRxMap =

A Hash of regexps for lists used for dynamic access.

{
  :ulist => UnorderedListRx,
  :olist => OrderedListRx,
  :dlist => DescriptionListRx,
  :colist => CalloutListRx
}
ColumnSpecRx =

Parses the column spec (i.e., colspec) for a table.

Examples

1*h,2*,^3e
/^(?:(\d+)\*)?([<^>](?:\.[<^>]?)?|(?:[<^>]?\.)?[<^>])?(\d+%?|~)?([a-z])?$/
CellSpecStartRx =

Parses the start and end of a cell spec (i.e., cellspec) for a table.

Examples

2.3+<.>m

FIXME use step-wise scan (or treetop) rather than this mega-regexp

/^[ \t]*(?:(\d+(?:\.\d*)?|(?:\d*\.)?\d+)([*+]))?([<^>](?:\.[<^>]?)?|(?:[<^>]?\.)?[<^>])?([a-z])?$/
CellSpecEndRx =
/[ \t]+(?:(\d+(?:\.\d*)?|(?:\d*\.)?\d+)([*+]))?([<^>](?:\.[<^>]?)?|(?:[<^>]?\.)?[<^>])?([a-z])?$/
CustomBlockMacroRx =

Matches the custom block macro pattern.

Examples

gist::123456[]

– NOTE we've relaxed the match for target to accomodate the short format (e.g., name::)

/^(#{CG_WORD}+)::(|\S|\S.*?\S)\[(.+)?\]$/
BlockMediaMacroRx =

Matches an image, video or audio block macro.

Examples

image::filename.png[Caption]
video::http://youtube.com/12345[Cats vs Dogs]
/^(image|video|audio)::(\S|\S.*?\S)\[(.+)?\]$/
BlockTocMacroRx =

Matches the TOC block macro.

Examples

toc::[]
toc::[levels=2]
/^toc::\[(.+)?\]$/
InlineAnchorRx =

Matches an anchor (i.e., id + optional reference text) in the flow of text.

Examples

[[idname]]
[[idname,Reference Text]]
anchor:idname[]
anchor:idname[Reference Text]
/(\\)?(?:\[\[([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)(?:, *(.+?))?\]\]|anchor:([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)\[(?:\]|(.*?[^\\])\]))/
InlineAnchorScanRx =

Scans for a non-escaped anchor (i.e., id + optional reference text) in the flow of text.

/(?:^|[^\\\[])\[\[([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)(?:, *(.+?))?\]\]|(?:^|[^\\])anchor:([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)\[(?:\]|(.*?[^\\])\])/
LeadingInlineAnchorRx =

Scans for a leading, non-escaped anchor (i.e., id + optional reference text).

/^\[\[([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)(?:, *(.+?))?\]\]/
InlineBiblioAnchorRx =

Matches a bibliography anchor at the start of the list item text (in a bibliography list).

Examples

[[[Fowler_1997]]] Fowler M. ...
/^\[\[\[([#{CC_ALPHA}_:][#{CC_WORD}:.-]*)(?:, *(.+?))?\]\]\]/
InlineEmailRx =

Matches an inline e-mail address.

doc.writer@example.com
%r(([\\>:/])?#{CG_WORD}[#{CC_WORD}.%+-]*@#{CG_ALNUM}[#{CC_ALNUM}.-]*\.#{CG_ALPHA}{2,4}\b)
InlineFootnoteMacroRx =

Matches an inline footnote macro, which is allowed to span multiple lines.

Examples

footnote:[text] (not referenceable)
footnote:id[text] (referenceable)
footnote:id[] (reference)
footnoteref:[id,text] (legacy)
footnoteref:[id] (legacy)
/\\?footnote(?:(ref):|:([\w-]+)?)\[(?:|(#{CC_ALL}*?[^\\]))\]/m
InlineImageMacroRx =

Matches an image or icon inline macro.

Examples

image:filename.png[Alt Text]
image:http://example.com/images/filename.png[Alt Text]
image:filename.png[More [Alt\] Text] (alt text becomes "More [Alt] Text")
icon:github[large]

NOTE be as non-greedy as possible by not allowing endline or left square bracket in target

/\\?i(?:mage|con):([^:\s\[](?:[^\n\[]*[^\s\[])?)\[(|#{CC_ALL}*?[^\\])\]/m
InlineIndextermMacroRx =

Matches an indexterm inline macro, which may span multiple lines.

Examples

indexterm:[Tigers,Big cats]
(((Tigers,Big cats)))
indexterm2:[Tigers]
((Tigers))
/\\?(?:(indexterm2?):\[(#{CC_ALL}*?[^\\])\]|\(\((#{CC_ALL}+?)\)\)(?!\)))/m
InlineKbdBtnMacroRx =

Matches either the kbd or btn inline macro.

Examples

kbd:[F3]
kbd:[Ctrl+Shift+T]
kbd:[Ctrl+\]]
kbd:[Ctrl,T]
btn:[Save]
/(\\)?(kbd|btn):\[(#{CC_ALL}*?[^\\])\]/m
InlineLinkRx =

Matches an implicit link and some of the link inline macro.

Examples

https://github.com
https://github.com[GitHub]
<https://github.com>
link:https://github.com[]

FIXME revisit! the main issue is we need different rules for implicit vs explicit

%r((^|link:|#{CG_BLANK}|&lt;|[>\(\)\[\];])(\\?(?:https?|file|ftp|irc)://[^\s\[\]<]*[^\s.,\[\]<])(?:\[(|#{CC_ALL}*?[^\\])\])?)m
InlineLinkMacroRx =

Match a link or e-mail inline macro.

Examples

link:path[label]
mailto:doc.writer@example.com[]

NOTE be as non-greedy as possible by not allowing space or left square bracket in target

/\\?(?:link|(mailto)):(|[^:\s\[][^\s\[]*)\[(|#{CC_ALL}*?[^\\])\]/m
MacroNameRx =

Matches the name of a macro.

/^#{CG_WORD}+$/
InlineStemMacroRx =

Matches a stem (and alternatives, asciimath and latexmath) inline macro, which may span multiple lines.

Examples

stem:[x != 0]
asciimath:[x != 0]
latexmath:[\sqrt{4} = 2]
/\\?(stem|(?:latex|ascii)math):([a-z]+(?:,[a-z]+)*)?\[(#{CC_ALL}*?[^\\])\]/m
InlineMenuMacroRx =

Matches a menu inline macro.

Examples

menu:File[Save As...]
menu:View[Page Style > No Style]
menu:View[Page Style, No Style]
/\\?menu:(#{CG_WORD}|[#{CC_WORD}&][^\n\[]*[^\s\[])\[ *(#{CC_ALL}*?[^\\])?\]/m
InlineMenuRx =

Matches an implicit menu inline macro.

Examples

"File > New..."
/\\?"([#{CC_WORD}&][^"]*?[ \n]+&gt;[ \n]+[^"]*)"/
InlinePassRx =

Matches an inline passthrough, which may span multiple lines.

Examples

+text+
`text` (compat)

NOTE we always capture the attributes so we know when to use compatible (i.e., legacy) behavior

{
  false => ['+', '`', /(^|[^#{CC_WORD};:])(?:\[([^\]]+)\])?(\\?(\+|`)(\S|\S#{CC_ALL}*?\S)\4)(?!#{CG_WORD})/m],
  true  => ['`', nil, /(^|[^`#{CC_WORD}])(?:\[([^\]]+)\])?(\\?(`)([^`\s]|[^`\s]#{CC_ALL}*?\S)\4)(?![`#{CC_WORD}])/m]
}
SinglePlusInlinePassRx =

Matches an inline plus passthrough spanning multiple lines, but only when it occurs directly inside constrained monospaced formatting in non-compat mode.

Examples

+text+
/^(\\)?\+(\S|\S#{CC_ALL}*?\S)\+$/m
InlinePassMacroRx =

Matches several variants of the passthrough inline macro, which may span multiple lines.

Examples

+++text+++
$$text$$
pass:quotes[text]

NOTE we have to support an empty pass:[] for compatibility with AsciiDoc Python

/(?:(?:(\\?)\[([^\]]+)\])?(\\{0,2})(\+\+\+?|\$\$)(#{CC_ALL}*?)\4|(\\?)pass:([a-z]+(?:,[a-z]+)*)?\[(|#{CC_ALL}*?[^\\])\])/m
InlineXrefMacroRx =

Matches an xref (i.e., cross-reference) inline macro, which may span multiple lines.

Examples

<<id,reftext>>
xref:id[reftext]

NOTE special characters have already been escaped, hence the entity references NOTE { is included in start characters to support target that begins with attribute reference in title content

%r(\\?(?:&lt;&lt;([#{CC_WORD}#/.:{]#{CC_ALL}*?)&gt;&gt;|xref:([#{CC_WORD}#/.:{]#{CC_ALL}*?)\[(?:\]|(#{CC_ALL}*?[^\\])\])))m
HardLineBreakRx =

NOTE In Ruby, ^ and $ always match start and end of line, respectively; JavaScript only does so in multiline mode

/^(.*) \+$/
MarkdownThematicBreakRx =

Matches a Markdown horizontal rule.

Examples

--- or - - -
*** or * * *
___ or _ _ _
/^ {0,3}([-*_])( *)\1\2\1$/
ExtLayoutBreakRx =

Matches an AsciiDoc or Markdown horizontal rule or AsciiDoc page break.

Examples

''' (horizontal rule)
<<< (page break)
--- or - - - (horizontal rule, Markdown)
*** or * * * (horizontal rule, Markdown)
___ or _ _ _ (horizontal rule, Markdown)
/^(?:'{3,}|<{3,}|([-*_])( *)\1\2\1)$/
BlankLineRx =

Matches consecutive blank lines.

Examples

one

two
/\n{2,}/
DataDelimiterRx =

Matches a comma or semi-colon delimiter.

Examples

one,two
three;four
/[,;]/
TrailingDigitsRx =

Matches one or more consecutive digits at the end of a line.

Examples

docbook45
html5
/\d+$/
EscapedSpaceRx =

Matches whitespace (space, tab, newline) escaped by a backslash.

Examples

three\ blind\ mice
/\\([ \t\n])/
ReplaceableTextRx =

Detects if text is a possible candidate for the replacements substitution.

/[&']|--|\.\.\.|\([CRT]M?\)/
SpaceDelimiterRx =

Examples

one two	 three   four
five	six

TODO change to /(?<!\)[ tn]+/ after dropping support for Ruby 1.8.7

/([^\\])[ \t\n]+/
SubModifierSniffRx =

Matches a + or - modifier in a subs list

/[+-]/
UnicodeCharScanRx =
/./u
UriSniffRx =

Detects strings that resemble URIs.

Examples

http://domain
https://domain
file:///path
data:info

not c:/sample.adoc or c:\sample.adoc
%r(^#{CG_ALPHA}[#{CC_ALNUM}.+-]+:/{0,2})
UriTerminatorRx =

Detects the end of an implicit URI in the text

Examples

(http://google.com)
&gt;http://google.com&lt;
(See http://google.com):
/[);:]$/
XmlSanitizeRx =

Detects XML tags

/<[^>]+>/
INTRINSIC_ATTRIBUTES =

end

{
  'startsb'    => '[',
  'endsb'      => ']',
  'vbar'       => '|',
  'caret'      => '^',
  'asterisk'   => '*',
  'tilde'      => '~',
  'plus'       => '&#43;',
  'backslash'  => '\\',
  'backtick'   => '`',
  'blank'      => '',
  'empty'      => '',
  'sp'         => ' ',
  'two-colons' => '::',
  'two-semicolons' => ';;',
  'nbsp'       => '&#160;',
  'deg'        => '&#176;',
  'zwsp'       => '&#8203;',
  'quot'       => '&#34;',
  'apos'       => '&#39;',
  'lsquo'      => '&#8216;',
  'rsquo'      => '&#8217;',
  'ldquo'      => '&#8220;',
  'rdquo'      => '&#8221;',
  'wj'         => '&#8288;',
  'brvbar'     => '&#166;',
  'cpp'        => 'C++',
  'amp'        => '&',
  'lt'         => '<',
  'gt'         => '>'
}
QUOTE_SUBS =
{
  false => quote_subs,
  true  => compat_quote_subs
}
REPLACEMENTS =

NOTE in Ruby 1.8.7, [^\] does not match start of line, so we need to match it explicitly order is significant

[
  # (C)
  [/\\?\(C\)/, '&#169;', :none],
  # (R)
  [/\\?\(R\)/, '&#174;', :none],
  # (TM)
  [/\\?\(TM\)/, '&#8482;', :none],
  # foo -- bar
  # FIXME this drops the endline if it appears at end of line
  [/(^|\n| |\\)--( |\n|$)/, '&#8201;&#8212;&#8201;', :none],
  # foo--bar
  [/(#{CG_WORD})\\?--(?=#{CG_WORD})/, '&#8212;&#8203;', :leading],
  # ellipsis
  [/\\?\.\.\./, '&#8230;&#8203;', :leading],
  # right single quote
  [/\\?`'/, '&#8217;', :none],
  # apostrophe (inside a word)
  [/(#{CG_ALNUM})\\?'(?=#{CG_ALPHA})/, '&#8217;', :leading],
  # right arrow ->
  [/\\?-&gt;/, '&#8594;', :none],
  # right double arrow =>
  [/\\?=&gt;/, '&#8658;', :none],
  # left arrow <-
  [/\\?&lt;-/, '&#8592;', :none],
  # left double arrow <=
  [/\\?&lt;=/, '&#8656;', :none],
  # restore entities
  [/\\?(&)amp;((?:[a-zA-Z][a-zA-Z]+\d{0,2}|#\d\d\d{0,4}|#x[\da-fA-F][\da-fA-F][\da-fA-F]{0,3});)/, '', :bounding]
]
VERSION =
'1.5.7.1'

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.convert(input, options = {}) ⇒ Object Also known as: render

Public: Parse the AsciiDoc source input into an Asciidoctor::Document and convert it to the specified backend format.

Accepts input as an IO (or StringIO), String or String Array object. If the input is a File, the object is expected to be opened for reading and is not closed afterwards by this method. Information about the file (filename, directory name, etc) gets assigned to attributes on the Document object.

If the :to_file option is true, and the input is a File, the output is written to a file adjacent to the input file, having an extension that corresponds to the backend format. Otherwise, if the :to_file option is specified, the file is written to that file. If :to_file is not an absolute path, it is resolved relative to :to_dir, if given, otherwise the Document#base_dir. If the target directory does not exist, it will not be created unless the :mkdirs option is set to true. If the file cannot be written because the target directory does not exist, or because it falls outside of the Document#base_dir in safe mode, an IOError is raised.

If the output is going to be written to a file, the header and footer are included unless specified otherwise (writing to a file implies creating a standalone document). Otherwise, the header and footer are not included by default and the converted result is returned.

input - the String AsciiDoc source filename options - a String, Array or Hash of options to control processing (default: {})

String and Array values are converted into a Hash.
See Asciidoctor::Document#initialize for details about options.

Returns the Document object if the converted String is written to a file, otherwise the converted String



1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
# File 'lib/asciidoctor.rb', line 1426

def convert input, options = {}
  options = options.dup
  options.delete(:parse)
  to_file = options.delete(:to_file)
  to_dir = options.delete(:to_dir)
  mkdirs = options.delete(:mkdirs) || false

  case to_file
  when true, nil
    write_to_same_dir = !to_dir && ::File === input
    stream_output = false
    write_to_target = to_dir
    to_file = nil
  when false
    write_to_same_dir = false
    stream_output = false
    write_to_target = false
    to_file = nil
  when '/dev/null'
    return self.load input, options
  else
    write_to_same_dir = false
    write_to_target = (stream_output = to_file.respond_to? :write) ? false : (options[:to_file] = to_file)
  end

  unless options.key? :header_footer
    options[:header_footer] = true if write_to_same_dir || write_to_target
  end

  # NOTE outfile may be controlled by document attributes, so resolve outfile after loading
  if write_to_same_dir
    input_path = ::File.expand_path input.path
    options[:to_dir] = (outdir = ::File.dirname input_path)
  elsif write_to_target
    if to_dir
      if to_file
        options[:to_dir] = ::File.dirname ::File.expand_path ::File.join to_dir, to_file
      else
        options[:to_dir] = ::File.expand_path to_dir
      end
    elsif to_file
      options[:to_dir] = ::File.dirname ::File.expand_path to_file
    end
  end

  # NOTE :to_dir is always set when outputting to a file
  # NOTE :to_file option only passed if assigned an explicit path
  doc = self.load input, options

  if write_to_same_dir # write to file in same directory
    outfile = ::File.join outdir, %(#{doc.attributes['docname']}#{doc.outfilesuffix})
    if outfile == input_path
      raise ::IOError, %(input file and output file cannot be the same: #{outfile})
    end
  elsif write_to_target # write to explicit file or directory
    working_dir = (options.key? :base_dir) ? (::File.expand_path options[:base_dir]) : ::Dir.pwd
    # QUESTION should the jail be the working_dir or doc.base_dir???
    jail = doc.safe >= SafeMode::SAFE ? working_dir : nil
    if to_dir
      outdir = doc.normalize_system_path(to_dir, working_dir, jail, :target_name => 'to_dir', :recover => false)
      if to_file
        outfile = doc.normalize_system_path(to_file, outdir, nil, :target_name => 'to_dir', :recover => false)
        # reestablish outdir as the final target directory (in the case to_file had directory segments)
        outdir = ::File.dirname outfile
      else
        outfile = ::File.join outdir, %(#{doc.attributes['docname']}#{doc.outfilesuffix})
      end
    elsif to_file
      outfile = doc.normalize_system_path(to_file, working_dir, jail, :target_name => 'to_dir', :recover => false)
      # establish outdir as the final target directory (in the case to_file had directory segments)
      outdir = ::File.dirname outfile
    end

    if ::File === input && outfile == (::File.expand_path input.path)
      raise ::IOError, %(input file and output file cannot be the same: #{outfile})
    end

    if mkdirs
      Helpers.mkdir_p outdir
    else
      # NOTE we intentionally refer to the directory as it was passed to the API
      raise ::IOError, %(target directory does not exist: #{to_dir} (hint: set mkdirs option)) unless ::File.directory? outdir
    end
  else # write to stream
    outfile = to_file
    outdir = nil
  end

  opts = outfile && !stream_output ? { 'outfile' => outfile, 'outdir' => outdir } : {}
  output = doc.convert opts

  if outfile
    doc.write output, outfile

    # NOTE document cannot control this behavior if safe >= SafeMode::SERVER
    # NOTE skip if stylesdir is a URI
    if !stream_output && doc.safe < SafeMode::SECURE && (doc.attr? 'linkcss') &&
        (doc.attr? 'copycss') && (doc.attr? 'basebackend-html') &&
        !((stylesdir = (doc.attr 'stylesdir')) && (Helpers.uriish? stylesdir))
      copy_asciidoctor_stylesheet = false
      copy_user_stylesheet = false
      if (stylesheet = (doc.attr 'stylesheet'))
        if DEFAULT_STYLESHEET_KEYS.include? stylesheet
          copy_asciidoctor_stylesheet = true
        elsif !(Helpers.uriish? stylesheet)
          copy_user_stylesheet = true
        end
      end
      copy_coderay_stylesheet = (doc.attr? 'source-highlighter', 'coderay') && (doc.attr 'coderay-css', 'class') == 'class'
      copy_pygments_stylesheet = (doc.attr? 'source-highlighter', 'pygments') && (doc.attr 'pygments-css', 'class') == 'class'
      if copy_asciidoctor_stylesheet || copy_user_stylesheet || copy_coderay_stylesheet || copy_pygments_stylesheet
        stylesoutdir = doc.normalize_system_path(stylesdir, outdir, doc.safe >= SafeMode::SAFE ? outdir : nil)
        if mkdirs
          Helpers.mkdir_p stylesoutdir
        else
          raise ::IOError, %(target stylesheet directory does not exist: #{stylesoutdir} (hint: set mkdirs option)) unless ::File.directory? stylesoutdir
        end

        if copy_asciidoctor_stylesheet
          Stylesheets.instance.write_primary_stylesheet stylesoutdir
        # FIXME should Stylesheets also handle the user stylesheet?
        elsif copy_user_stylesheet
          if (stylesheet_src = (doc.attr 'copycss')).empty?
            stylesheet_src = doc.normalize_system_path stylesheet
          else
            # NOTE in this case, copycss is a source location (but cannot be a URI)
            stylesheet_src = doc.normalize_system_path stylesheet_src
          end
          stylesheet_dest = doc.normalize_system_path stylesheet, stylesoutdir, (doc.safe >= SafeMode::SAFE ? outdir : nil)
          # NOTE don't warn if src can't be read and dest already exists (see #2323)
          if stylesheet_src != stylesheet_dest && (stylesheet_data = doc.read_asset stylesheet_src,
              :warn_on_failure => !(::File.file? stylesheet_dest), :label => 'stylesheet')
            ::IO.write stylesheet_dest, stylesheet_data
          end
        end

        if copy_coderay_stylesheet
          Stylesheets.instance.write_coderay_stylesheet stylesoutdir
        elsif copy_pygments_stylesheet
          Stylesheets.instance.write_pygments_stylesheet stylesoutdir, (doc.attr 'pygments-style')
        end
      end
    end
    doc
  else
    output
  end
end

.convert_file(filename, options = {}) ⇒ Object Also known as: render_file

Public: Parse the contents of the AsciiDoc source file into an Asciidoctor::Document and convert it to the specified backend format.

input - the String AsciiDoc source filename options - a String, Array or Hash of options to control processing (default: {})

String and Array values are converted into a Hash.
See Asciidoctor::Document#initialize for details about options.

Returns the Document object if the converted String is written to a file, otherwise the converted String



1588
1589
1590
# File 'lib/asciidoctor.rb', line 1588

def convert_file filename, options = {}
  ::File.open(filename, 'rb') {|file| self.convert file, options }
end

.load(input, options = {}) ⇒ Object

Public: Parse the AsciiDoc source input into a Document

Accepts input as an IO (or StringIO), String or String Array object. If the input is a File, the object is expected to be opened for reading and is not closed afterwards by this method. Information about the file (filename, directory name, etc) gets assigned to attributes on the Document object.

input - the AsciiDoc source as a IO, String or Array. options - a String, Array or Hash of options to control processing (default: {})

String and Array values are converted into a Hash.
See {Document#initialize} for details about these options.

Returns the Document



1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
# File 'lib/asciidoctor.rb', line 1284

def load input, options = {}
  options = options.dup

  if (timings = options[:timings])
    timings.start :read
  end

  if (logger = options[:logger]) && logger != LoggerManager.logger
    LoggerManager.logger = logger
  end

  if !(attrs = options[:attributes])
    attrs = {}
  elsif ::Hash === attrs || (::RUBY_ENGINE_JRUBY && ::Java::JavaUtil::Map === attrs)
    attrs = attrs.dup
  elsif ::Array === attrs
    attrs, attrs_arr = {}, attrs
    attrs_arr.each do |entry|
      k, v = entry.split '=', 2
      attrs[k] = v || ''
    end
  elsif ::String === attrs
    # condense and convert non-escaped spaces to null, unescape escaped spaces, then split on null
    attrs, attrs_arr = {}, attrs.gsub(SpaceDelimiterRx, %(\\1#{NULL})).gsub(EscapedSpaceRx, '\1').split(NULL)
    attrs_arr.each do |entry|
      k, v = entry.split '=', 2
      attrs[k] = v || ''
    end
  elsif (attrs.respond_to? :keys) && (attrs.respond_to? :[])
    # convert it to a Hash as we know it
    attrs = ::Hash[attrs.keys.map {|k| [k, attrs[k]] }]
  else
    raise ::ArgumentError, %(illegal type for attributes option: #{attrs.class.ancestors * ' < '})
  end

  lines = nil
  if ::File === input
    # TODO cli checks if input path can be read and is file, but might want to add check to API
    input_path = ::File.expand_path input.path
    # See https://reproducible-builds.org/specs/source-date-epoch/
    # NOTE Opal can't call key? on ENV
    input_mtime = ::ENV['SOURCE_DATE_EPOCH'] ? ::Time.at(Integer ::ENV['SOURCE_DATE_EPOCH']).utc : input.mtime
    lines = input.readlines
    # hold off on setting infile and indir until we get a better sense of their purpose
    attrs['docfile'] = input_path
    attrs['docdir'] = ::File.dirname input_path
    attrs['docname'] = Helpers.basename input_path, (attrs['docfilesuffix'] = ::File.extname input_path)
    if (docdate = attrs['docdate'])
      attrs['docyear'] ||= ((docdate.index '-') == 4 ? (docdate.slice 0, 4) : nil)
    else
      docdate = attrs['docdate'] = (input_mtime.strftime '%Y-%m-%d')
      attrs['docyear'] ||= input_mtime.year.to_s
    end
    doctime = (attrs['doctime'] ||= input_mtime.strftime('%H:%M:%S %Z'))
    attrs['docdatetime'] = %(#{docdate} #{doctime})
  elsif input.respond_to? :readlines
    # NOTE tty, pipes & sockets can't be rewound, but can't be sniffed easily either
    # just fail the rewind operation silently to handle all cases
    begin
      input.rewind
    rescue
    end
    lines = input.readlines
  elsif ::String === input
    lines = ::RUBY_MIN_VERSION_2 ? input.lines : input.each_line.to_a
  elsif ::Array === input
    lines = input.dup
  else
    raise ::ArgumentError, %(unsupported input type: #{input.class})
  end

  if timings
    timings.record :read
    timings.start :parse
  end

  options[:attributes] = attrs
  doc = options[:parse] == false ? (Document.new lines, options) : (Document.new lines, options).parse

  timings.record :parse if timings
  doc
rescue => ex
  begin
    context = %(asciidoctor: FAILED: #{attrs['docfile'] || '<stdin>'}: Failed to load AsciiDoc document)
    if ex.respond_to? :exception
      # The original message must be explicitely preserved when wrapping a Ruby exception
      wrapped_ex = ex.exception %(#{context} - #{ex.message})
      # JRuby automatically sets backtrace, but not MRI
      wrapped_ex.set_backtrace ex.backtrace
    else
      # Likely a Java exception class
      wrapped_ex = ex.class.new context, ex
      wrapped_ex.stack_trace = ex.stack_trace
    end
  rescue
    wrapped_ex = ex
  end
  raise wrapped_ex
end

.load_file(filename, options = {}) ⇒ Object

Public: Parse the contents of the AsciiDoc source file into an Asciidoctor::Document

input - the String AsciiDoc source filename options - a String, Array or Hash of options to control processing (default: {})

String and Array values are converted into a Hash.
See Asciidoctor::Document#initialize for details about options.

Returns the Asciidoctor::Document



1392
1393
1394
# File 'lib/asciidoctor.rb', line 1392

def load_file filename, options = {}
  ::File.open(filename, 'rb') {|file| self.load file, options }
end

Instance Method Details

#const_missing(name) ⇒ Object

Internal: Automatically load the Asciidoctor::Extensions module.

Requires the Asciidoctor::Extensions module if the name is :Extensions. Otherwise, delegates to the super method.

This method provides the same functionality as using autoload on Asciidoctor::Extensions, except that the constant isn't recognized as defined prior to it being loaded.

Returns the resolved constant, if resolved, otherwise nothing.



1605
1606
1607
1608
1609
1610
1611
1612
# File 'lib/asciidoctor.rb', line 1605

def const_missing name
  if name == :Extensions
    require 'asciidoctor/extensions'
    Extensions
  else
    super
  end
end