= Custom Syntax Highlighter Adapter
:navtitle: Custom Adapter
:apidoc-base: {url-api-gems}/asciidoctor/{release-version}/Asciidoctor
:apidoc-syntax-highlighter: {apidoc-base}/SyntaxHighlighter
:apidoc-syntax-highlighter-base: {apidoc-syntax-highlighter}/Base
:apidoc-syntax-highlighter-for: {apidoc-syntax-highlighter}/Factory#for-instance_method

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.

[#new]
== Create a new adapter

To implement a new adapter, you must create a class that extends the {apidoc-syntax-highlighter-base}[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`.

.Syntax highlighter adapter for Prism.js
[,ruby]
----
include::example$prism-syntax-highlighter.rb[]
----

Save this code to a file named [.path]_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]
== 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 {apidoc-syntax-highlighter-for}[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.

Here's the basic template for customizing an existing adapter:

[,ruby]
----
class CustomAdapter < (Asciidoctor::SyntaxHighlighter.for 'rouge')
  register_for 'rouge'

  # override methods go here
end
----

Let's look at some examples of how to customize a built-in adapter.

=== docinfo

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).

.Extended syntax highlighter adapter for Pygments
[,ruby]
----
include::example$extended-pygments-syntax-highlighter.rb[]
----

Save this code to a file named [.path]_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, instead, you wanted to modify the built-in adapter to honor the location of a custom stylesheet specified by the `pygments-stylesheet` attribute, you can do so by extending the adapter and overriding the `docinfo` method.

[,ruby]
----
include::example$pygments-syntax-highlighter-with-custom-stylesheet.rb[]
----

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.

=== highlight

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`.

[,ruby]
----
include::example$always-number-lines.rb[]
----

=== create_formatter (Rouge)

When using Rouge as the syntax highlighter, you can customize the formatter by overriding the `create_formatter` method.
This allows you to add custom logic for handling certain tokens in the source language.

Let's assume that you want to look for bare URLs in code comments and translate them into links (i.e., autolinks), just like in AsciiDoc.
You can do that by weaving extra logic into the formatter that looks for tokens in the `Comment` category and applies a substitution to the value.

[,ruby]
----
include::example$autolink-urls-in-comments.rb[]
----

Since the formatter has access to all the tokens in the code identified by the syntax highlighter, this technique opens up a lot of possibilities.
For example, you could look for the `Type` token in the `Keyword` category in Java code and create a link to the API docs.
The `lang` argument to the `create_formatter` method lets you know the source language (e.g., `java`) to which the tokens belong.

To study the logic you may be interesting in overriding, browse the code for the https://github.com/asciidoctor/asciidoctor/tree/{page-origin-refname}/lib/asciidoctor/syntax_highlighter[built-in syntax highlighter adapters].
