Custom Syntax Highlighter Adapter

You can integrate additional syntax highlighters into Asciidoctor by implementing and registering a syntax highlighter adapter. You can either write a new adapter from scratch or you can extend and even replace one of the built-in adapters.

Create a new adapter

To implement a new adapter, you must create a class that extends the Asciidoctor::SyntaxHighlighter::Base class, register the adapter for a value of the source-highlighter attribute, and implement the required methods. Which methods are required depends on whether the adapter is for a client-side (runs in the browser) or build-time (runs when the document is converted) syntax highlighter.

Here’s an example of how to write and register a syntax highlighter adapter for the Prism.js syntax highlighting library. Prism.js is a client-side syntax highlighter, meaning it runs in the browser. That means the adapter only has to implement methods that pertain to client-side syntax highlighting, which include format, docinfo?, and docinfo.

Example 1. Syntax highlighter adapter for Prism.js
class PrismSyntaxHighlighter < Asciidoctor::SyntaxHighlighter::Base
  register_for 'prism'

  def format node, lang, opts
    opts[:transform] = proc do |pre, code|
      code['class'] = %(language-#{lang}) if lang

  def docinfo? location
    location == :footer

  def docinfo location, doc, opts
    base_url = doc.attr 'prismdir', %(#{opts[:cdn_base_url]}/prism/1.15.0)
    slash = opts[:self_closing_tag_slash]
    unless (theme_name = doc.attr 'prism-style', 'prism') == 'prism'
      theme_name = %(prism-#{theme_name})
    %(<link rel="stylesheet" href="#{base_url}/themes/#{theme_name}.min.css"#{slash}>
<script src="#{base_url}/prism.min.js"></script>
<script src="#{base_url}/components/prism-ruby.min.js"></script>)

Save this code to a file named prism-syntax-highlighter.rb. Then, require this file when invoking Asciidoctor and set source-highlighter=prism to activate it:

$ asciidoctor -r ./prism-syntax-highlighter -a source-highlighter=prism document.adoc

You can also define an adapter for a syntax highlighter that runs during conversion. We’ll look at doing that while also extending a built-in adapter.

Extend an existing adapter

Instead of creating a new adapter, you can customize a built-in adapter by extending it, overriding its behavior, and optionally replacing it.

To extend an adapter, you need to look up a reference to the built-in adapter by name using the Asciidoctor::SyntaxHighlighter.for method, create a class that extends it, register the adapter with a unique name (or the same name, if you want to replace it), and override any methods that provide the behavior you want to modify.

Let’s override the adapter for Pygments to prevent it from adding a stylesheet to the HTML (presumably because the styles will be provided by a different stylesheet).

Example 2. Extended syntax highlighter adapter for Pygments
class ExtendedPygmentsSyntaxHighlighter < (Asciidoctor::SyntaxHighlighter.for 'pygments')
  register_for 'pygments'

  def docinfo? location

Save this code to a file named extended-pygments-syntax-highlighter.rb. Then, require this file when invoking Asciidoctor, setting source-highlighter=pygments to activate it, as you would normally do:

$ asciidoctor -r ./extended-pygments-syntax-highlighter.rb -a source-highlighter=pygments document.adoc

If you want to decorate built-in behavior, you can invoke the super method anywhere inside the method to delegate to the behavior provided by the built-in adapter.

Let’s say you always want lines to be numbered, regardless of the setting in the document. You can do so by overriding the highlight method, setting the :number_lines key on the opts argument, then delegating back to the built-in adapter using super.

def highlight node, source, lang, opts
  opts[:number_lines] = true

To study the logic you may be interesting in overridding, browse the code for the built-in syntax highlighter adapters.