Module: Asciidoctor
- Defined in:
- lib/asciidoctor.rb,
lib/asciidoctor/rx.rb,
lib/asciidoctor/list.rb,
lib/asciidoctor/load.rb,
lib/asciidoctor/block.rb,
lib/asciidoctor/table.rb,
lib/asciidoctor/inline.rb,
lib/asciidoctor/parser.rb,
lib/asciidoctor/reader.rb,
lib/asciidoctor/writer.rb,
lib/asciidoctor/convert.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/rouge_ext.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/html5.rb,
lib/asciidoctor/converter/manpage.rb,
lib/asciidoctor/converter/docbook5.rb,
lib/asciidoctor/converter/template.rb,
lib/asciidoctor/syntax_highlighter.rb,
lib/asciidoctor/converter/composite.rb,
lib/asciidoctor/syntax_highlighter/rouge.rb,
lib/asciidoctor/syntax_highlighter/coderay.rb,
lib/asciidoctor/syntax_highlighter/prettify.rb,
lib/asciidoctor/syntax_highlighter/pygments.rb,
lib/asciidoctor/syntax_highlighter/highlightjs.rb,
lib/asciidoctor/syntax_highlighter/html_pipeline.rb
Overview
The main application interface (API) for Asciidoctor. This API provides methods to parse AsciiDoc content and convert it to various output formats using built-in or third-party converters or Tilt-supported templates.
An AsciiDoc document can be as simple as a single line of content, though it more commonly starts with a document header that declares the document title and document attribute definitions. The document header is then followed by zero or more section titles, optionally nested, to organize the paragraphs, blocks, lists, etc. of the document.
By default, the processor converts the AsciiDoc document to HTML 5 using a built-in converter. However, this behavior can be changed by specifying a different backend (e.g., docbook
). A backend is a keyword for an output format (e.g., DocBook). That keyword, in turn, is used to select a converter, which carries out the request to convert the document to that format.
In addition to this API, Asciidoctor also provides a command-line interface (CLI) named asciidoctor
for converting AsciiDoc content. See the provided man(ual) page for usage and options.
Defined Under Namespace
Modules: Cli, Compliance, Converter, Extensions, LoggerManager, Logging, RougeExt, Rx, SafeMode, Substitutors, SyntaxHighlighter, 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_OPAL =
alias the RUBY_ENGINE constant inside the Asciidoctor namespace and define a precomputed alias for runtime
(RUBY_ENGINE = ::RUBY_ENGINE) == 'opal'
- ROOT_DIR =
The absolute root directory of the Asciidoctor RubyGem
::File.dirname ::File.absolute_path __dir__
- LIB_DIR =
The absolute lib directory of the Asciidoctor RubyGem
::File.join ROOT_DIR, 'lib'
- DATA_DIR =
The absolute data directory of the Asciidoctor RubyGem
::File.join ROOT_DIR, 'data'
- USER_HOME =
The user’s home directory, as best we can determine it IMPORTANT this rescue is required for running Asciidoctor on GitHub.com
::Dir.home rescue (::ENV['HOME'] || ::Dir.pwd)
- LF =
The newline character used for output; stored in constant table as an optimization
?\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
- UTF_8 =
Alias UTF_8 encoding for convenience / speed
::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]
- FILE_READ_MODE =
The mode to use when opening a file for reading
RUBY_ENGINE_OPAL ? 'r' : 'rb:UTF-8:UTF-8'
- URI_READ_MODE =
The mode to use when opening a URI for reading
FILE_READ_MODE
- FILE_WRITE_MODE =
The mode to use when opening a file for writing
RUBY_ENGINE_OPAL ? 'w' : 'wb:UTF-8'
- 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 =
A map of file extensions that are recognized as AsciiDoc documents TODO .txt should be deprecated
{ '.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 =
::Set.new.tap {|accum| ADMONITION_STYLES.each {|s| accum << s.chr } }
- 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], '++++' => [: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 =
{}.tap {|accum| DELIMITED_BLOCKS.each_key {|k| accum[k.slice 0, 2] = true } }
- DELIMITED_BLOCK_TAILS =
{}.tap {|accum| DELIMITED_BLOCKS.each_key {|k| accum[k] = k[k.length - 1] if k.length == 4 } }
- CAPTION_ATTRIBUTE_NAMES =
NOTE the ‘figure’ key as a string is historical and used by image blocks
{ example: 'example-caption', 'figure' => 'figure-caption', listing: 'listing-caption', table: 'table-caption' }
- 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.py allows + to be preceded by TAB; Asciidoctor does not
' +'
- LINE_CONTINUATION =
' \\'
- LINE_CONTINUATION_LEGACY =
' +'
- BLOCK_MATH_DELIMITERS =
{ asciimath: ['\$', '\$'], latexmath: ['\[', '\]'], }
- INLINE_MATH_DELIMITERS =
{ asciimath: ['\$', '\$'], latexmath: ['\(', '\)'], }
- FONT_AWESOME_VERSION =
'4.7.0'
- HIGHLIGHT_JS_VERSION =
'9.18.3'
- MATHJAX_VERSION =
'2.7.9'
- DEFAULT_ATTRIBUTES =
{ 'appendix-caption' => 'Appendix', 'appendix-refsig' => 'Appendix', 'caution-caption' => 'Caution', 'chapter-refsig' => 'Chapter', #'encoding' => 'UTF-8', 'example-caption' => 'Example', 'figure-caption' => 'Figure', 'important-caption' => 'Important', 'last-update-label' => 'Last updated', #'listing-caption' => 'Listing', 'note-caption' => 'Note', 'part-refsig' => 'Part', #'preface-title' => 'Preface', 'prewrap' => '', 'sectids' => '', 'section-refsig' => 'Section', 'table-caption' => 'Table', 'tip-caption' => 'Tip', 'toc-placement' => 'auto', 'toc-title' => 'Table of Contents', 'untitled-label' => 'Untitled', 'version-label' => 'Version', 'warning-caption' => 'Warning', }
- FLEXIBLE_ATTRIBUTES =
attributes which be changed throughout the flow of the document (e.g., sectnums)
['sectnums']
- INTRINSIC_ATTRIBUTES =
{ 'startsb' => '[', 'endsb' => ']', 'vbar' => '|', 'caret' => '^', 'asterisk' => '*', 'tilde' => '~', 'plus' => '+', 'backslash' => '\\', 'backtick' => '`', 'blank' => '', 'empty' => '', 'sp' => ' ', 'two-colons' => '::', 'two-semicolons' => ';;', 'nbsp' => ' ', 'deg' => '°', 'zwsp' => '​', 'quot' => '"', 'apos' => ''', 'lsquo' => '‘', 'rsquo' => '’', 'ldquo' => '“', 'rdquo' => '”', 'wj' => '⁠', 'brvbar' => '¦', 'pp' => '++', 'cpp' => 'C++', 'amp' => '&', 'lt' => '<', 'gt' => '>' }
- CC_ALL =
CC_ALL is any character, including newlines (must be accompanied by multiline regexp flag)
'.'
- CC_ANY =
CC_ANY is any character except newlines
'.'
- CC_EOL =
'$'
- CC_ALPHA =
CG_ALPHA = '\p{Alpha}'
- CC_ALNUM =
CG_ALNUM = '\p{Alnum}'
- CG_BLANK =
'\p{Blank}'
- CC_WORD =
CG_WORD = '\p{Word}'
- QUOTE_SUBS =
{}.tap do |accum| # unconstrained quotes:: can appear anywhere # constrained quotes:: must be bordered by non-word characters # NOTE these substitutions are processed in the order they appear here and # the order in which they are replaced is important accum[false] = normal = [ # **strong** [:strong, :unconstrained, /\\?(?:\[([^\]]+)\])?\*\*(#{CC_ALL}+?)\*\*/m], # *strong* [:strong, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]]+)\])?\*(\S|\S#{CC_ALL}*?\S)\*(?!#{CG_WORD})/m], # "`double-quoted`" [:double, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]]+)\])?"`(\S|\S#{CC_ALL}*?\S)`"(?!#{CG_WORD})/m], # '`single-quoted`' [:single, :constrained, /(^|[^#{CC_WORD};:`}])(?:\[([^\]]+)\])?'`(\S|\S#{CC_ALL}*?\S)`'(?!#{CG_WORD})/m], # ``monospaced`` [:monospaced, :unconstrained, /\\?(?:\[([^\]]+)\])?``(#{CC_ALL}+?)``/m], # `monospaced` [:monospaced, :constrained, /(^|[^#{CC_WORD};:"'`}])(?:\[([^\]]+)\])?`(\S|\S#{CC_ALL}*?\S)`(?![#{CC_WORD}"'`])/m], # __emphasis__ [:emphasis, :unconstrained, /\\?(?:\[([^\]]+)\])?__(#{CC_ALL}+?)__/m], # _emphasis_ [:emphasis, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]]+)\])?_(\S|\S#{CC_ALL}*?\S)_(?!#{CG_WORD})/m], # ##mark## (referred to in AsciiDoc.py as unquoted) [:mark, :unconstrained, /\\?(?:\[([^\]]+)\])?##(#{CC_ALL}+?)##/m], # #mark# (referred to in AsciiDoc.py as unquoted) [:mark, :constrained, /(^|[^#{CC_WORD}&;:}])(?:\[([^\]]+)\])?#(\S|\S#{CC_ALL}*?\S)#(?!#{CG_WORD})/m], # ^superscript^ [:superscript, :unconstrained, /\\?(?:\[([^\]]+)\])?\^(\S+?)\^/], # ~subscript~ [:subscript, :unconstrained, /\\?(?:\[([^\]]+)\])?~(\S+?)~/] ] accum[true] = compat = normal.drop 0 # ``quoted'' compat[2] = [:double, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]]+)\])?``(\S|\S#{CC_ALL}*?\S)''(?!#{CG_WORD})/m] # `quoted' compat[3] = [:single, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]]+)\])?`(\S|\S#{CC_ALL}*?\S)'(?!#{CG_WORD})/m] # ++monospaced++ compat[4] = [:monospaced, :unconstrained, /\\?(?:\[([^\]]+)\])?\+\+(#{CC_ALL}+?)\+\+/m] # +monospaced+ compat[5] = [:monospaced, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]]+)\])?\+(\S|\S#{CC_ALL}*?\S)\+(?!#{CG_WORD})/m] # #unquoted# #compat[8] = [:unquoted, *compat[8][1..-1]] # ##unquoted## #compat[9] = [:unquoted, *compat[9][1..-1]] # 'emphasis' compat.insert 3, [:emphasis, :constrained, /(^|[^#{CC_WORD};:}])(?:\[([^\]]+)\])?'(\S|\S#{CC_ALL}*?\S)'(?!#{CG_WORD})/m] end
- REPLACEMENTS =
NOTE order of replacements is significant
[ # (C) [/\\?\(C\)/, '©', :none], # (R) [/\\?\(R\)/, '®', :none], # (TM) [/\\?\(TM\)/, '™', :none], # foo -- bar (where either space character can be a newline) # NOTE this necessarily drops the newline if replacement appears at end of line [/(?: |\n|^|\\)--(?: |\n|$)/, ' — ', :none], # foo--bar [/(#{CG_WORD})\\?--(?=#{CG_WORD})/, '—​', :leading], # ellipsis [/\\?\.\.\./, '…​', :none], # right single quote [/\\?`'/, '’', :none], # apostrophe (inside a word) [/(#{CG_ALNUM})\\?'(?=#{CG_ALPHA})/, '’', :leading], # right arrow -> [/\\?->/, '→', :none], # right double arrow => [/\\?=>/, '⇒', :none], # left arrow <- [/\\?<-/, '←', :none], # left double arrow <= [/\\?<=/, '⇐', :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] ]
- AuthorInfoLineRx =
Matches the author info line immediately following the document title.
/^(#{CG_WORD}[#{CC_WORD}\-'.]*)(?: +(#{CG_WORD}[#{CC_WORD}\-'.]*))?(?: +(#{CG_WORD}[#{CC_WORD}\-'.]*))?(?: +<([^>]+)>)?$/
- AuthorDelimiterRx =
Matches the delimiter that separates multiple authors.
/;(?: |$)/
- RevisionInfoLineRx =
Matches the revision info line, which appears immediately following the author info line beneath the document title.
/^(?:[^\d{]*(#{CC_ANY}*?),)? *(?!:)(#{CC_ANY}*?)(?: *(?!^),?: *(#{CC_ANY}*))?$/
- ManpageTitleVolnumRx =
Matches the title and volnum in the manpage doctype.
/^(#{CC_ANY}+?) *\( *(#{CC_ANY}+?) *\)$/
- ManpageNamePurposeRx =
Matches the name and purpose in the manpage doctype.
/^(#{CC_ANY}+?) +- +(#{CC_ANY}+)$/
- ConditionalDirectiveRx =
Matches a conditional preprocessor directive (e.g., ifdef, ifndef, ifeval and endif).
/^(\\)?(ifdef|ifndef|ifeval|endif)::(\S*?(?:([,+])\S*?)?)\[(#{CC_ANY}+)?\]$/
- EvalExpressionRx =
Matches a restricted (read as safe) eval expression.
/^(#{CC_ANY}+?) *([=!><]=|[><]) *(#{CC_ANY}+)$/
- IncludeDirectiveRx =
Matches an include preprocessor directive.
/^(\\)?include::([^\s\[](?:[^\[]*[^\s\[])?)\[(#{CC_ANY}+)?\]$/
- TagDirectiveRx =
Matches a trailing tag directive in an include file.
/\b(?:tag|(e)nd)::(\S+?)\[\](?=$|[ \r])/m
- AttributeEntryRx =
Matches a document attribute entry.
/^:(!?#{CG_WORD}[^:]*):(?:[ \t]+(#{CC_ANY}*))?$/
- InvalidAttributeNameCharsRx =
Matches invalid characters in an attribute name.
/[^#{CC_WORD}-]/
- AttributeEntryPassMacroRx =
NOTE 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.
/(\\)?\{(#{CG_WORD}[#{CC_WORD}-]*|(set|counter2?):#{CC_ANY}+?)(\\)?\}/
- BlockAnchorRx =
Matches an anchor (i.e., id + optional reference text) on a line above a block.
/^\[\[(?:|([#{CC_ALPHA}_:][#{CC_WORD}\-:.]*)(?:, *(#{CC_ANY}+))?)\]\]$/
- BlockAttributeListRx =
Matches an attribute list above a block element.
/^\[(|[#{CC_WORD}.#%{,"']#{CC_ANY}*)\]$/
- 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_ANY}*|\[(?:|[#{CC_ALPHA}_:][#{CC_WORD}\-:.]*(?:, *#{CC_ANY}+)?)\])\]$/
- BlockTitleRx =
Matches a title above a block.
/^\.(\.?[^ \t.]#{CC_ANY}*)$/
- AdmonitionParagraphRx =
Matches an admonition label at the start of a paragraph.
/^(#{ADMONITION_STYLES.to_a.join '|'}):[ \t]+/
- LiteralParagraphRx =
Matches a literal paragraph, which is a line of text preceded by at least one space.
/^([ \t]+#{CC_ANY}*)$/
- AtxSectionTitleRx =
Matches an Atx (single-line) section title.
/^(=={0,5})[ \t]+(#{CC_ANY}+?)(?:[ \t]+\1)?$/
- ExtAtxSectionTitleRx =
Matches an extended Atx section title that includes support for the Markdown variant.
/^(=={0,5}|#\#{0,5})[ \t]+(#{CC_ANY}+?)(?:[ \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.
/^((?!\.)#{CC_ANY}*?#{CG_ALNUM}#{CC_ANY}*)$/
- InlineSectionAnchorRx =
Matches an anchor (i.e., id + optional reference text) inside a section title.
/ (\\)?\[\[([#{CC_ALPHA}_:][#{CC_WORD}\-:.]*)(?:, *(#{CC_ANY}+))?\]\]$/
- 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}\-.]+?/
- SectionLevelStyleRx =
Matches an explicit section level style like sect1
/^sect\d$/
- 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. IMPORTANT if this regexp does not agree with the regexp for each list type, the parser will hang.
%r(^(?:[ \t]*(?:-|\*\**|\.\.*|\u2022|\d+\.|[a-zA-Z]\.|[IVXivx]+\))[ \t]|(?!//[^/])[ \t]*[^ \t]#{CC_ANY}*?(?::::{0,2}|;;)(?:$|[ \t])|<(?:\d+|\.)>[ \t]))
- UnorderedListRx =
Matches an unordered list item (one level for hyphens, up to 5 levels for asterisks).
/^[ \t]*(-|\*\**|\u2022)[ \t]+(#{CC_ANY}*)$/
- OrderedListRx =
Matches an ordered list item (explicit numbering or up to 5 consecutive dots).
/^[ \t]*(\.\.*|\d+\.|[a-zA-Z]\.|[IVXivx]+\))[ \t]+(#{CC_ANY}*)$/
- 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.
%r(^(?!//[^/])[ \t]*([^ \t]#{CC_ANY}*?)(:::{0,2}|;;)(?:$|[ \t]+(#{CC_ANY}*)$))
- DescriptionListSiblingRx =
Matches a sibling description list item (excluding the delimiter specified by the key). NOTE must skip line comment when looking for sibling list item
{ '::' => %r(^(?!//[^/])[ \t]*([^ \t]#{CC_ANY}*?[^:]|[^ \t:])(::)(?:$|[ \t]+(#{CC_ANY}*)$)), ':::' => %r(^(?!//[^/])[ \t]*([^ \t]#{CC_ANY}*?[^:]|[^ \t:])(:::)(?:$|[ \t]+(#{CC_ANY}*)$)), '::::' => %r(^(?!//[^/])[ \t]*([^ \t]#{CC_ANY}*?[^:]|[^ \t:])(::::)(?:$|[ \t]+(#{CC_ANY}*)$)), ';;' => %r(^(?!//[^/])[ \t]*([^ \t]#{CC_ANY}*?)(;;)(?:$|[ \t]+(#{CC_ANY}*)$)) }
- CalloutListRx =
Matches a callout list item.
/^<(\d+|\.)>[ \t]+(#{CC_ANY}*)$/
- CalloutExtractRx =
Matches a callout reference inside literal text.
%r(((?://|#|--|;;) ?)?(\\)?<!?(|--)(\d+|\.)\3>(?=(?: ?\\?<!?\3(?:\d+|\.)\3>)*$))
- CalloutExtractRxt =
'(\\\\)?<()(\\d+|\\.)>(?=(?: ?\\\\?<(?:\\d+|\\.)>)*$)'
- CalloutExtractRxMap =
::Hash.new {|h, k| h[k] = /(#{k.empty? ? '' : "#{::Regexp.escape k} ?"})?#{CalloutExtractRxt}/ }
- 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(((?://|#|--|;;) ?)?(\\)?<!?(|--)(\d+|\.)\3>(?=(?: ?\\?<!?\3(?:\d+|\.)\3>)*#{CC_EOL}))
- CalloutSourceRxt =
"(\\\\)?<()(\\d+|\\.)>(?=(?: ?\\\\?<(?:\\d+|\\.)>)*#{CC_EOL})"
- CalloutSourceRxMap =
::Hash.new {|h, k| h[k] = /(#{k.empty? ? '' : "#{::Regexp.escape k} ?"})?#{CalloutSourceRxt}/ }
- 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.
/^(?:(\d+)\*)?([<^>](?:\.[<^>]?)?|(?:[<^>]?\.)?[<^>])?(\d+%?|~)?([a-z])?$/
- CellSpecStartRx =
Parses the start and end of a cell spec (i.e., cellspec) for a table.
/^[ \t]*(?:(\d+(?:\.\d*)?|(?:\d*\.)?\d+)([*+]))?([<^>](?:\.[<^>]?)?|(?:[<^>]?\.)?[<^>])?([a-z])?$/
- CellSpecEndRx =
/[ \t]+(?:(\d+(?:\.\d*)?|(?:\d*\.)?\d+)([*+]))?([<^>](?:\.[<^>]?)?|(?:[<^>]?\.)?[<^>])?([a-z])?$/
- CustomBlockMacroRx =
Matches the custom block macro pattern.
/^(#{CG_WORD}[#{CC_WORD}-]*)::(|\S|\S#{CC_ANY}*?\S)\[(#{CC_ANY}+)?\]$/
- BlockMediaMacroRx =
Matches an image, video or audio block macro.
/^(image|video|audio)::(\S|\S#{CC_ANY}*?\S)\[(#{CC_ANY}+)?\]$/
- BlockTocMacroRx =
Matches the TOC block macro.
/^toc::\[(#{CC_ANY}+)?\]$/
- InlineAnchorRx =
Matches an anchor (i.e., id + optional reference text) in the flow of text.
/(\\)?(?:\[\[([#{CC_ALPHA}_:][#{CC_WORD}\-:.]*)(?:, *(#{CC_ANY}+?))?\]\]|anchor:([#{CC_ALPHA}_:][#{CC_WORD}\-:.]*)\[(?:\]|(#{CC_ANY}*?[^\\])\]))/
- InlineAnchorScanRx =
Scans for a non-escaped anchor (i.e., id + optional reference text) in the flow of text.
/(?:^|[^\\\[])\[\[([#{CC_ALPHA}_:][#{CC_WORD}\-:.]*)(?:, *(#{CC_ANY}+?))?\]\]|(?:^|[^\\])anchor:([#{CC_ALPHA}_:][#{CC_WORD}\-:.]*)\[(?:\]|(#{CC_ANY}*?[^\\])\])/
- LeadingInlineAnchorRx =
Scans for a leading, non-escaped anchor (i.e., id + optional reference text).
/^\[\[([#{CC_ALPHA}_:][#{CC_WORD}\-:.]*)(?:, *(#{CC_ANY}+?))?\]\]/
- InlineBiblioAnchorRx =
Matches a bibliography anchor at the start of the list item text (in a bibliography list).
/^\[\[\[([#{CC_ALPHA}_:][#{CC_WORD}\-:.]*)(?:, *(#{CC_ANY}+?))?\]\]\]/
- InlineEmailRx =
Matches an inline e-mail address.
%r(([\\>:/])?#{CG_WORD}(?:&|[#{CC_WORD}\-.%+])*@#{CG_ALNUM}[#{CC_ALNUM}_\-.]*\.[a-zA-Z]{2,5}\b)
- InlineFootnoteMacroRx =
Matches an inline footnote macro, which is allowed to span multiple lines.
%r(\\?footnote(?:(ref):|:([#{CC_WORD}-]+)?)\[(?:|(#{CC_ALL}*?[^\\]))\](?!</a>))m
- InlineImageMacroRx =
Matches an image or icon inline macro.
/\\?i(?:mage|con):([^:\s\[](?:[^\n\[]*[^\s\[])?)\[(|#{CC_ALL}*?[^\\])\]/m
- InlineIndextermMacroRx =
Matches an indexterm inline macro, which may span multiple lines.
/\\?(?:(indexterm2?):\[(#{CC_ALL}*?[^\\])\]|\(\((#{CC_ALL}+?)\)\)(?!\)))/m
- InlineKbdBtnMacroRx =
Matches either the kbd or btn inline macro.
/(\\)?(kbd|btn):\[(#{CC_ALL}*?[^\\])\]/m
- InlineLinkRx =
NOTE In JavaScript, a back reference succeeds if not set; invert the logic to give it a match to refute
%r((^|link:|#{CG_BLANK}|\\?<()|[>\(\)\[\];"'])(\\?(?:https?|file|ftp|irc)://)(?:([^\s\[\]]+)\[(|#{CC_ALL}*?[^\\])\]|\2([^\s]+?)>|([^\s\[\]<]*([^\s,.?!\[\]<\)]))))m
- InlineLinkMacroRx =
Match a link or e-mail inline macro.
/\\?(?:link|(mailto)):(|[^:\s\[][^\s\[]*)\[(|#{CC_ALL}*?[^\\])\]/m
- MacroNameRx =
Matches the name of a macro.
/^#{CG_WORD}[#{CC_WORD}-]*$/
- InlineStemMacroRx =
Matches a stem (and alternatives, asciimath and latexmath) inline macro, which may span multiple lines.
/\\?(stem|(?:latex|ascii)math):([a-z]+(?:,[a-z-]+)*)?\[(#{CC_ALL}*?[^\\])\]/m
- InlineMenuMacroRx =
Matches a menu inline macro.
/\\?menu:(#{CG_WORD}|[#{CC_WORD}&][^\n\[]*[^\s\[])\[ *(?:|(#{CC_ALL}*?[^\\]))\]/m
- InlineMenuRx =
Matches an implicit menu inline macro.
/\\?"([#{CC_WORD}&][^"]*?[ \n]+>[ \n]+[^"]*)"/
- InlinePassRx =
Matches an inline passthrough, which may span multiple lines.
{ false => ['+', '-]', /((?:^|[^#{CC_WORD};:\\])(?=(\[)|\+)|\\(?=\[)|(?=\\\+))(?:\2(x-|[^\]]+ x-)\]|(?:\[([^\]]+)\])?(?=(\\)?\+))(\5?(\+|`)(\S|\S#{CC_ALL}*?\S)\7)(?!#{CG_WORD})/m], true => ['`', nil, /(^|[^`#{CC_WORD}])(?:(\Z)()|\[([^\]]+)\](?=(\\))?)?(\5?(`)([^`\s]|[^`\s]#{CC_ALL}*?\S)\7)(?![`#{CC_WORD}])/m], }
- InlinePassMacroRx =
Matches several variants of the passthrough inline macro, which may span multiple lines.
/(?:(?:(\\?)\[([^\]]+)\])?(\\{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.
%r(\\?(?:<<([#{CC_WORD}#/.:{]#{CC_ALL}*?)>>|xref:([#{CC_WORD}#/.:{]#{CC_ALL}*?)\[(?:\]|(#{CC_ALL}*?[^\\])\])))m
- HardLineBreakRx =
NOTE In Ruby, ^ and $ always match start and end of line
/^(.*) \+$/
- MarkdownThematicBreakRx =
Matches a Markdown horizontal rule.
/^ {0,3}([-*_])( *)\1\2\1$/
- ExtLayoutBreakRx =
Matches an AsciiDoc or Markdown horizontal rule or AsciiDoc page break.
/^(?:'{3,}|<{3,}|([-*_])( *)\1\2\1)$/
- BlankLineRx =
Matches consecutive blank lines.
/\n{2,}/
- EscapedSpaceRx =
Matches whitespace (space, tab, newline) escaped by a backslash.
/\\([ \t\n])/
- ReplaceableTextRx =
Detects if text is a possible candidate for the replacements substitution.
/[&']|--|\.\.\.|\([CRT]M?\)/
- SpaceDelimiterRx =
Matches a whitespace delimiter, a sequence of spaces, tabs, and/or newlines. Matches the parsing rules of %w strings in Ruby.
/([^\\])[ \t\n]+/
- SubModifierSniffRx =
Matches a + or - modifier in a subs list
/[+-]/
- TrailingDigitsRx =
Matches one or more consecutive digits at the end of a line.
/\d+$/
- UriSniffRx =
%r(\A#{CG_ALPHA}[#{CC_ALNUM}.+-]+:/{0,2})
- XmlSanitizeRx =
Detects XML tags
/<[^>]+>/
- VERSION =
'2.0.23'
Class Method Summary collapse
-
.convert(input, options = {}) ⇒ Object
(also: render)
Parse the AsciiDoc source input into an Asciidoctor::Document and convert it to the specified backend format.
-
.convert_file(filename, options = {}) ⇒ Object
(also: render_file)
Parse the contents of the AsciiDoc source file into an Asciidoctor::Document and convert it to the specified backend format.
-
.load(input, options = {}) ⇒ Object
Parse the AsciiDoc source input into a Document.
-
.load_file(filename, options = {}) ⇒ Object
Parse the contents of the AsciiDoc source file into an Asciidoctor::Document.
Class Method Details
.convert(input, options = {}) ⇒ Object Also known as: render
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.
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/asciidoctor/convert.rb', line 34 def convert input, = {} ( = .merge).delete :parse to_dir = .delete :to_dir mkdirs = .delete :mkdirs case (to_file = .delete :to_file) when true, nil unless (write_to_target = to_dir) sibling_path = ::File.absolute_path input.path if ::File === input end to_file = nil when false to_file = nil when '/dev/null' return load input, else [:to_file] = write_to_target = to_file unless (stream_output = to_file.respond_to? :write) end unless .key? :standalone if sibling_path || write_to_target [:standalone] = .fetch :header_footer, true elsif .key? :header_footer [:standalone] = [:header_footer] end end # NOTE outfile may be controlled by document attributes, so resolve outfile after loading if sibling_path [:to_dir] = outdir = ::File.dirname sibling_path elsif write_to_target if to_dir if to_file [:to_dir] = ::File.dirname ::File. to_file, to_dir else [:to_dir] = ::File. to_dir end elsif to_file [:to_dir] = ::File.dirname ::File. 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 = load input, if sibling_path # write to file in same directory outfile = ::File.join outdir, %(#{doc.attributes['docname']}#{doc.outfilesuffix}) raise ::IOError, %(input file and output file cannot be the same: #{outfile}) if outfile == sibling_path elsif write_to_target # write to explicit file or directory working_dir = (.key? :base_dir) ? (::File. [: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.absolute_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 if outfile && !stream_output output = doc.convert 'outfile' => outfile, 'outdir' => outdir else output = doc.convert end 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.basebackend? 'html') && !((stylesdir = (doc.attr 'stylesdir')) && (Helpers.uriish? stylesdir)) 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_syntax_hl_stylesheet = (syntax_hl = doc.syntax_highlighter) && (syntax_hl.write_stylesheet? doc) if copy_asciidoctor_stylesheet || copy_user_stylesheet || copy_syntax_hl_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') == '' || stylesheet_src == true 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.to_s 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') if (stylesheet_outdir = ::File.dirname stylesheet_dest) != stylesoutdir && !(::File.directory? stylesheet_outdir) if mkdirs Helpers.mkdir_p stylesheet_outdir else raise ::IOError, %(target stylesheet directory does not exist: #{stylesheet_outdir} (hint: set :mkdirs option)) end end ::File.write stylesheet_dest, stylesheet_data, mode: FILE_WRITE_MODE end end syntax_hl.write_stylesheet doc, stylesoutdir if copy_syntax_hl_stylesheet end end doc else output end end |
.convert_file(filename, options = {}) ⇒ Object Also known as: render_file
Parse the contents of the AsciiDoc source file into an Asciidoctor::Document and convert it to the specified backend format.
189 190 191 |
# File 'lib/asciidoctor/convert.rb', line 189 def convert_file filename, = {} ::File.open(filename, FILE_READ_MODE) {|file| convert file, } end |
.load(input, options = {}) ⇒ Object
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.
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/asciidoctor/load.rb', line 17 def load input, = {} = .merge if (timings = [:timings]) timings.start :read end if (.key? :logger) && (logger = [:logger]) != LoggerManager.logger LoggerManager.logger = logger || NullLogger.new end if !(attrs = [:attributes]) attrs = {} elsif ::Hash === attrs attrs = attrs.merge elsif (defined? ::Java::JavaUtil::Map) && ::Java::JavaUtil::Map === attrs attrs = attrs.dup elsif ::Array === attrs attrs = {}.tap do |accum| attrs.each do |entry| k, _, v = entry.partition '=' accum[k] = v end end elsif ::String === attrs # condense and convert non-escaped spaces to null, unescape escaped spaces, then split on null attrs = {}.tap do |accum| attrs.gsub(SpaceDelimiterRx, '\1' + NULL).gsub(EscapedSpaceRx, '\1').split(NULL).each do |entry| k, _, v = entry.partition '=' accum[k] = v end end elsif (attrs.respond_to? :keys) && (attrs.respond_to? :[]) # coerce attrs to a real Hash attrs = {}.tap {|accum| attrs.keys.each {|k| accum[k] = attrs[k] } } else raise ::ArgumentError, %(illegal type for attributes option: #{attrs.class.ancestors.join ' < '}) end if ::File === input # File#mtime on JRuby 9.1 for Windows doesn't honor TZ environment variable; see https://github.com/jruby/jruby/issues/6659 [:input_mtime] = RUBY_ENGINE == 'jruby' ? (::Time.at input.mtime.to_i) : input.mtime # NOTE defer setting infile and indir until we get a better sense of their purpose # TODO cli checks if input path can be read and is file, but might want to add check to API too attrs['docfile'] = input_path = ::File.absolute_path input.path attrs['docdir'] = ::File.dirname input_path attrs['docname'] = Helpers.basename input_path, (attrs['docfilesuffix'] = Helpers.extname input_path) source = input.read elsif input.respond_to? :read # 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 input.rewind rescue nil source = input.read elsif ::String === input source = input elsif ::Array === input source = input.drop 0 elsif input raise ::ArgumentError, %(unsupported input type: #{input.class}) end if timings timings.record :read timings.start :parse end [:attributes] = attrs doc = [:parse] == false ? (Document.new source, ) : (Document.new source, ).parse timings.record :parse if timings doc rescue => e begin context = %(asciidoctor: FAILED: #{attrs['docfile'] || '<stdin>'}: Failed to load AsciiDoc document) if e.respond_to? :exception # The original message must be explicitly preserved when wrapping a Ruby exception wrapped_e = e.exception %(#{context} - #{e.}) # JRuby automatically sets backtrace; MRI did not until 2.6 wrapped_e.set_backtrace e.backtrace else # Likely a Java exception class wrapped_e = e.class.new context, e wrapped_e.stack_trace = e.stack_trace end rescue wrapped_e = e end raise wrapped_e end |
.load_file(filename, options = {}) ⇒ Object
Parse the contents of the AsciiDoc source file into an Asciidoctor::Document
115 116 117 |
# File 'lib/asciidoctor/load.rb', line 115 def load_file filename, = {} ::File.open(filename, FILE_READ_MODE) {|file| load file, } end |