summaryrefslogtreecommitdiff
path: root/docs/modules/extensions
diff options
context:
space:
mode:
Diffstat (limited to 'docs/modules/extensions')
-rw-r--r--docs/modules/extensions/nav.adoc11
-rw-r--r--docs/modules/extensions/pages/block-macro-processor.adoc51
-rw-r--r--docs/modules/extensions/pages/block-processor.adoc48
-rw-r--r--docs/modules/extensions/pages/compound-block-processor.adoc56
-rw-r--r--docs/modules/extensions/pages/docinfo-processor.adoc37
-rw-r--r--docs/modules/extensions/pages/include-processor.adoc52
-rw-r--r--docs/modules/extensions/pages/index.adoc51
-rw-r--r--docs/modules/extensions/pages/inline-macro-processor.adoc51
-rw-r--r--docs/modules/extensions/pages/postprocessor.adoc35
-rw-r--r--docs/modules/extensions/pages/preprocessor.adoc68
-rw-r--r--docs/modules/extensions/pages/register.adoc26
-rw-r--r--docs/modules/extensions/pages/tree-processor.adoc86
12 files changed, 572 insertions, 0 deletions
diff --git a/docs/modules/extensions/nav.adoc b/docs/modules/extensions/nav.adoc
new file mode 100644
index 00000000..e209f801
--- /dev/null
+++ b/docs/modules/extensions/nav.adoc
@@ -0,0 +1,11 @@
+* xref:index.adoc[]
+** xref:register.adoc[]
+** xref:preprocessor.adoc[]
+** xref:tree-processor.adoc[]
+** xref:postprocessor.adoc[]
+** xref:docinfo-processor.adoc[]
+** xref:block-processor.adoc[]
+** xref:compound-block-processor.adoc[]
+** xref:block-macro-processor.adoc[]
+** xref:inline-macro-processor.adoc[]
+** xref:include-processor.adoc[]
diff --git a/docs/modules/extensions/pages/block-macro-processor.adoc b/docs/modules/extensions/pages/block-macro-processor.adoc
new file mode 100644
index 00000000..1d2ac22d
--- /dev/null
+++ b/docs/modules/extensions/pages/block-macro-processor.adoc
@@ -0,0 +1,51 @@
+= Block Macro Processor Extension Example
+:navtitle: Block Macro Processor
+
+Purpose::
+Create a block macro named `gist` for embedding a gist.
+
+== sample-with-gist-macro.adoc
+
+[source,asciidoc]
+----
+.My Gist
+gist::123456[]
+----
+
+== GistBlockMacro
+
+[source,ruby]
+----
+require 'asciidoctor'
+require 'asciidoctor/extensions'
+
+class GistBlockMacro < Asciidoctor::Extensions::BlockMacroProcessor
+ use_dsl
+
+ named :gist
+
+ def process parent, target, attrs
+ title_html = (attrs.has_key? 'title') ?
+ %(<div class="title">#{attrs['title']}</div>\n) : nil
+
+ html = %(<div class="openblock gist">
+#{title_html}<div class="content">
+<script src="https://gist.github.com/#{target}.js"></script>
+</div>
+</div>)
+
+ create_pass_block parent, html, attrs, subs: nil
+ end
+end
+----
+
+== Usage
+
+[source,ruby]
+----
+Asciidoctor::Extensions.register do
+ block_macro GistBlockMacro if document.basebackend? 'html'
+end
+
+Asciidoctor.convert_file 'sample-with-gist.adoc', safe: :safe
+----
diff --git a/docs/modules/extensions/pages/block-processor.adoc b/docs/modules/extensions/pages/block-processor.adoc
new file mode 100644
index 00000000..7352af31
--- /dev/null
+++ b/docs/modules/extensions/pages/block-processor.adoc
@@ -0,0 +1,48 @@
+= Block Processor Extension Example
+:navtitle: Block Processor
+
+Purpose::
+Register a custom block style named `shout` that uppercases all the words and converts periods to exclamation points.
+
+== sample-with-shout-block.adoc
+
+[source,asciidoc]
+----
+[shout]
+The time is now. Get a move on.
+----
+
+== ShoutBlock
+
+[source,ruby]
+----
+require 'asciidoctor'
+require 'asciidoctor/extensions'
+
+class ShoutBlock < Asciidoctor::Extensions::BlockProcessor
+ PeriodRx = /\.(?= |$)/
+
+ use_dsl
+
+ named :shout
+ on_context :paragraph
+ name_positional_attributes 'vol'
+ parse_content_as :simple
+
+ def process parent, reader, attrs
+ volume = ((attrs.delete 'vol') || 1).to_i
+ create_paragraph parent, (reader.lines.map {|l| l.upcase.gsub PeriodRx, '!' * volume }), attrs
+ end
+end
+----
+
+== Usage
+
+[source,ruby]
+----
+Asciidoctor::Extensions.register do
+ block ShoutBlock
+end
+
+Asciidoctor.convert_file 'sample-with-shout-block.adoc', safe: :safe
+----
diff --git a/docs/modules/extensions/pages/compound-block-processor.adoc b/docs/modules/extensions/pages/compound-block-processor.adoc
new file mode 100644
index 00000000..cd88a3b6
--- /dev/null
+++ b/docs/modules/extensions/pages/compound-block-processor.adoc
@@ -0,0 +1,56 @@
+= Compound Block Processor Example
+:navtitle: Compound Block Processor
+
+Purpose::
+Register a custom block named `collapsible` that transforms a listing block into a compound block composed of the following:
+
+* an example block with the collapsible option enabled
+* the original listing block
+* the listing block is promoted to a source block if a language is specified using the second positional attribute.
+
+.sample-with-collapsible-block.adoc
+[source,asciidoc]
+....
+.Show JSON
+[collapsible,json]
+----
+{
+ "foo": "bar"
+}
+----
+....
+
+.collapsible-block.rb
+[source,ruby]
+----
+class CollapsibleBlock < Asciidoctor::Extensions::BlockProcessor
+ enable_dsl
+ on_context :listing
+ positional_attributes 'language'
+
+ def process parent, reader, attrs
+ lang = attrs.delete 'language'
+ attrs['title'] ||= 'Show Listing'
+ example = create_example_block parent, [], attrs, content_model: :compound
+ example.set_option 'collapsible'
+ listing = create_listing_block example, reader.readlines, nil
+ if lang
+ listing.style = 'source'
+ listing.set_attr 'language', lang
+ listing.commit_subs
+ end
+ example << listing
+ example
+ end
+end
+
+Asciidoctor::Extensions.register do
+ block CollapsibleBlock, :collapsible
+end
+----
+
+.Usage
+ $ asciidoctor -r ./collapsible-block.rb sample-with-collapsible-block.adoc
+
+NOTE: This extension mimics the builtin `collapsible` option on the example block, but consolidates it to a single block.
+The purpose of this extension is to show how to assemble a compound block in an extension.
diff --git a/docs/modules/extensions/pages/docinfo-processor.adoc b/docs/modules/extensions/pages/docinfo-processor.adoc
new file mode 100644
index 00000000..636b24a0
--- /dev/null
+++ b/docs/modules/extensions/pages/docinfo-processor.adoc
@@ -0,0 +1,37 @@
+= Docinfo Processor Extension Example
+:navtitle: Docinfo Processor
+
+Purpose::
+Appends the Google Analytics tracking code to the bottom of an HTML document.
+
+== GoogleAnalyticsDocinfoProcessor
+
+[source,ruby]
+----
+class GoogleAnalyticsDocinfoProcessor < Asciidoctor::Extensions::DocinfoProcessor
+ use_dsl
+ at_location :footer
+ def process document
+ return unless (ga_account_id = document.attr 'google-analytics-account')
+ %(<script>
+(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
+ga('create','#{ga_account_id}','auto');
+ga('send','pageview');
+</script>)
+ end
+end
+----
+
+== Usage
+
+[source,ruby]
+----
+Asciidoctor::Extensions.register do
+ docinfo_processor GoogleAnalyticsDocinfoProcessor
+end
+
+Asciidoctor.convert_file 'sample.adoc', safe: :safe, attributes: 'UA-ABCXYZ123'
+----
diff --git a/docs/modules/extensions/pages/include-processor.adoc b/docs/modules/extensions/pages/include-processor.adoc
new file mode 100644
index 00000000..ee1a220a
--- /dev/null
+++ b/docs/modules/extensions/pages/include-processor.adoc
@@ -0,0 +1,52 @@
+= Include Processor Extension Example
+:navtitle: Include Processor
+
+Purpose::
+Include a file from a URI.
+
+TIP: Asciidoctor supports including content from a URI out of the box if you set the `allow-uri-read` attribute (not available if the safe mode is `secure`).
+
+== sample-with-uri-include.adoc
+
+[source,asciidoc]
+....
+:source-highlighter: coderay
+
+.Gemfile
+[source,ruby]
+----
+\include::https://raw.githubusercontent.com/asciidoctor/asciidoctor/master/Gemfile[]
+----
+....
+
+== UriIncludeProcessor
+
+[source,ruby]
+----
+require 'asciidoctor'
+require 'asciidoctor/extensions'
+require 'open-uri'
+
+class UriIncludeProcessor < Asciidoctor::Extensions::IncludeProcessor
+ def handles? target
+ (target.start_with? 'http://') or (target.start_with? 'https://')
+ end
+
+ def process doc, reader, target, attributes
+ content = (open target).readlines
+ reader.push_include content, target, target, 1, attributes
+ reader
+ end
+end
+----
+
+== Usage
+
+[source,ruby]
+----
+Asciidoctor::Extensions.register do
+ include_processor UriIncludeProcessor
+end
+
+Asciidoctor.convert_file 'sample-with-uri-include.adoc', safe: :safe
+----
diff --git a/docs/modules/extensions/pages/index.adoc b/docs/modules/extensions/pages/index.adoc
new file mode 100644
index 00000000..33b35755
--- /dev/null
+++ b/docs/modules/extensions/pages/index.adoc
@@ -0,0 +1,51 @@
+= Extensions
+:url-exten-lab: https://github.com/asciidoctor/asciidoctor-extensions-lab
+
+Extensions are central to the success of AsciiDoc because they open up the language to new use cases.
+Asciidoctor provides an extension API that offers a superset of extension points.
+As a result, extensions in Asciidoctor are easy to write, powerful, and simple to distribute.
+
+Asciidoctor also allows extensions to be written using the full power of a programming language (whether it be Ruby, Java, Groovy or JavaScript).
+You don't have to shave yaks to get the functionality you want, and you can distribute the extension using defacto-standard packaging mechanisms like RubyGems or JARs.
+
+== Available extension points
+
+Asciidoctor provides the following extension points:
+
+Preprocessor::
+Processes the raw source lines before they are passed to the parser.
+See xref:preprocessor.adoc[].
+
+Tree processor::
+Processes the [.class]#Asciidoctor::Document# (AST) once parsing is complete.
+See xref:tree-processor.adoc[].
+
+Postprocessor::
+Processes the output after the document has been converted, but before it's written to disk.
+See xref:postprocessor.adoc[].
+
+Docinfo Processor::
+Adds additional content to the header or footer regions of the generated document.
+See xref:docinfo-processor.adoc[].
+
+Block processor::
+Processes a block of content marked with a custom block style (i.e., `[custom]`). (similar to an AsciiDoc filter)
+See xref:block-processor.adoc[].
+
+Compound block processor::
+Register a custom block named `collapsible` that transforms a listing block into a compound block.
+See xref:compound-block-processor.adoc[].
+
+Block macro processor::
+Registers a custom block macro and processes it (e.g., `gist::12345[]`).
+See xref:block-macro-processor.adoc[].
+
+Inline macro processor::
+Registers a custom inline macro and processes it (e.g., `btn:[Save]`).
+See xref:inline-macro-processor.adoc[].
+
+Include processor::
+Processes the `include::<filename>[]` directive.
+See xref:include-processor.adoc[].
+
+There are additional extension examples in the {url-exten-lab}[Asciidoctor extensions lab^].
diff --git a/docs/modules/extensions/pages/inline-macro-processor.adoc b/docs/modules/extensions/pages/inline-macro-processor.adoc
new file mode 100644
index 00000000..b16b4854
--- /dev/null
+++ b/docs/modules/extensions/pages/inline-macro-processor.adoc
@@ -0,0 +1,51 @@
+= Inline Macro Processor Extension Example
+:navtitle: Inline Macro Processor
+
+Purpose::
+Create an inline macro named `man` that links to a manpage.
+
+== sample-with-man-link.adoc
+
+[source,asciidoc]
+----
+See man:gittutorial[7] to get started.
+----
+
+== ManpageInlineMacro
+
+[source,ruby]
+----
+require 'asciidoctor'
+require 'asciidoctor/extensions'
+
+class ManInlineMacro < Asciidoctor::Extensions::InlineMacroProcessor
+ use_dsl
+
+ named :man
+ name_positional_attributes 'volnum'
+
+ def process parent, target, attrs
+ text = manname = target
+ suffix = ''
+ target = %(#{manname}.html)
+ suffix = if (volnum = attrs['volnum'])
+ "(#{volnum})"
+ else
+ nil
+ end
+ parent.document.register :links, target
+ %(#{(create_anchor parent, text, type: :link, target: target).convert}#{suffix})
+ end
+end
+----
+
+== Usage
+
+[source,ruby]
+----
+Asciidoctor::Extensions.register do
+ inline_macro ManInlineMacro
+end
+
+Asciidoctor.convert_file 'sample-with-man-link.adoc', safe: :safe
+----
diff --git a/docs/modules/extensions/pages/postprocessor.adoc b/docs/modules/extensions/pages/postprocessor.adoc
new file mode 100644
index 00000000..130ca547
--- /dev/null
+++ b/docs/modules/extensions/pages/postprocessor.adoc
@@ -0,0 +1,35 @@
+= Postprocessor Extension Example
+:navtitle: Postprocessor
+
+Purpose::
+Insert copyright text in the footer.
+
+== CopyrightFooterPostprocessor
+
+[source,ruby]
+----
+class CopyrightFooterPostprocessor < Asciidoctor::Extensions::Postprocessor
+ def process document, output
+ content = (document.attr 'copyright') || 'Copyright Acme, Inc.'
+ if document.basebackend? 'html'
+ replacement = %(<div id="footer-text">\\1<br>\n#{content}\n</div>)
+ output = output.sub(/<div id="footer-text">(.*?)<\/div>/m, replacement)
+ elsif document.basebackend? 'docbook'
+ replacement = %(<simpara>#{content}</simpara>\n\\1)
+ output = output.sub(/(<\/(?:article|book)>)/, replacement)
+ end
+ output
+ end
+end
+----
+
+== Usage
+
+[source,ruby]
+----
+Asciidoctor::Extensions.register do
+ postprocessor CopyrightFooterPostprocessor
+end
+
+Asciidoctor.convert_file 'sample-with-copyright-footer.adoc', safe: :safe
+----
diff --git a/docs/modules/extensions/pages/preprocessor.adoc b/docs/modules/extensions/pages/preprocessor.adoc
new file mode 100644
index 00000000..d1cc95f1
--- /dev/null
+++ b/docs/modules/extensions/pages/preprocessor.adoc
@@ -0,0 +1,68 @@
+= Preprocessor Extension Example
+:navtitle: Preprocessor
+
+Purpose::
+Skim off front matter from the top of the document that gets used by site generators like Jekyll and Awestruct.
+
+== sample-with-front-matter.adoc
+
+[source,asciidoc]
+----
+tags: [announcement, website]
+---
+= Document Title
+
+content
+
+[subs=+attributes]
+.Captured front matter
+....
+---
+{front-matter}
+---
+....
+----
+
+== FrontMatterPreprocessor
+
+[source,ruby]
+----
+require 'asciidoctor'
+require 'asciidoctor/extensions'
+
+class FrontMatterPreprocessor < Asciidoctor::Extensions::Preprocessor
+ def process document, reader
+ lines = reader.lines # get raw lines
+ return reader if lines.empty?
+ front_matter = []
+ if lines.first.chomp == '---'
+ original_lines = lines.dup
+ lines.shift
+ while !lines.empty? && lines.first.chomp != '---'
+ front_matter << lines.shift
+ end
+
+ if (first = lines.first).nil? || first.chomp != '---'
+ lines = original_lines
+ else
+ lines.shift
+ document.attributes['front-matter'] = front_matter.join.chomp
+ # advance the reader by the number of lines taken
+ (front_matter.length + 2).times { reader.advance }
+ end
+ end
+ reader
+ end
+end
+----
+
+== Usage
+
+[source,ruby]
+----
+Asciidoctor::Extensions.register do
+ preprocessor FrontMatterPreprocessor
+end
+
+Asciidoctor.convert_file 'sample-with-front-matter.adoc', safe: :safe
+----
diff --git a/docs/modules/extensions/pages/register.adoc b/docs/modules/extensions/pages/register.adoc
new file mode 100644
index 00000000..468398ef
--- /dev/null
+++ b/docs/modules/extensions/pages/register.adoc
@@ -0,0 +1,26 @@
+= Register Extensions
+
+== Register one or more extensions
+
+These extensions are registered per document using a callback that feels like a DSL:
+
+[source,ruby]
+----
+Asciidoctor::Extensions.register do |document|
+ preprocessor FrontMatterPreprocessor
+ tree_processor ShellSessionTreeProcessor
+ postprocessor CopyrightFooterPostprocessor
+ docinfo_processor TrackingCodeDocinfoProcessor if document.basebackend? 'html'
+ block ShoutBlock
+ block_macro GistBlockMacro if document.basebackend? 'html'
+ inline_macro ManInlineMacro
+ include_processor UriIncludeProcessor
+end
+----
+
+CAUTION: Extension classes must be defined outside of the register block.
+Once an extension class is registered, it is frozen, preventing further modification.
+If you define an extension class inside the register block, it will result in an error on subsequent invocations.
+
+You can register more than one processor of each type, though you can only have one processor per custom block or macro.
+Each registered class is instantiated when the [.class]#Asciidoctor::Document# is created.
diff --git a/docs/modules/extensions/pages/tree-processor.adoc b/docs/modules/extensions/pages/tree-processor.adoc
new file mode 100644
index 00000000..ac97cbc7
--- /dev/null
+++ b/docs/modules/extensions/pages/tree-processor.adoc
@@ -0,0 +1,86 @@
+= Tree Processor Extension Example
+:navtitle: Tree Processor
+
+Purpose::
+Detect literal blocks that contain shell commands, strip the prompt character and style the command using CSS in such a way that the prompt character cannot be selected (as seen on help.github.com).
+
+== sample-with-shell-session.adoc
+
+[source,asciidoc]
+----
+ $ echo "Hello, World!"
+ > Hello, World!
+
+ $ gem install asciidoctor
+----
+
+== ShellSessionTreeProcessor
+
+[source,ruby]
+----
+class ShellSessionTreeProcessor < Asciidoctor::Extensions::TreeProcessor
+ def process document
+ return unless document.blocks?
+ process_blocks document
+ nil
+ end
+
+ def process_blocks node
+ node.blocks.each_with_index do |block, i|
+ if block.context == :literal &&
+ (((first_line = block.lines.first).start_with? '$ ') ||
+ (first_line.start_with? '> '))
+ node.blocks[i] = convert_to_terminal_listing block
+ else
+ process_blocks block if block.blocks?
+ end
+ end
+ end
+
+ def convert_to_terminal_listing block
+ attrs = block.attributes
+ attrs['role'] = 'terminal'
+ prompt_attr = (attrs.has_key? 'prompt') ?
+ %( data-prompt="#{block.sub_specialchars attrs['prompt']}") : nil
+ lines = block.lines.map do |line|
+ line = block.sub_specialchars line.chomp
+ if line.start_with? '$ '
+ %(<span class="command"#{prompt_attr}>#{line[2..-1]}</span>)
+ elsif line.start_with? '&gt; '
+ %(<span class="output">#{line[5..-1]}</span>)
+ else
+ line
+ end
+ end
+ create_listing_block block.document, lines * EOL, attrs, subs: nil
+ end
+end
+----
+
+== Usage
+
+[source,ruby]
+----
+Asciidoctor::Extensions.register do
+ tree_processor ShellSessionTreeProcessor
+end
+
+Asciidoctor.convert_file 'sample-with-shell-session.adoc', safe: :safe
+----
+
+////
+In the example below the TreeProcessor examines the block contents looking for the `// (*)` suffix and rewrites the line so that Asciidoctor formats it appropriately.
+
+[source,java]
+----
+protected void configure(HttpSecurity http) throws Exception {
+ http
+ .authorizeRequests()
+ .antMatchers("/resources/**").permitAll() // (*)
+ .anyRequest().authenticated()
+ .and()
+ .formLogin()
+ .loginPage("/login")
+ .permitAll();
+----
+////