diff --git a/README.adoc b/README.adoc index 1484c146..6c02f690 100644 --- a/README.adoc +++ b/README.adoc @@ -109,6 +109,9 @@ Highlights source blocks using the highlight command. ImplicitApidocInlineMacro, link:lib/implicit-apidoc-inline-macro.rb[]:: Adds an inline macro for linking to the Javadoc of a class in the Java EE API. +IndirIncludeProcessor, link:lib/indir-include-processor.rb[]:: +Adds a variable "indir", pointing at the directory of included asciidoc files. + LicenseUrlDocinfoProcessor, link:lib/license-url-docinfoprocessor.rb[]:: Adds a link to the license specified by the `license` attribute to the document header. diff --git a/lib/indir-include-processor.rb b/lib/indir-include-processor.rb new file mode 100644 index 00000000..86caa964 --- /dev/null +++ b/lib/indir-include-processor.rb @@ -0,0 +1,8 @@ +## +# Require this file to register and use the IndirIncludeProcessor. + +RUBY_ENGINE == 'opal' ? (require 'indir-include-processor/extension') : (require_relative 'indir-include-processor/extension') + +Asciidoctor::Extensions.register do + include_processor IndirIncludeProcessor +end diff --git a/lib/indir-include-processor/example/master.adoc b/lib/indir-include-processor/example/master.adoc new file mode 100644 index 00000000..66a5b524 --- /dev/null +++ b/lib/indir-include-processor/example/master.adoc @@ -0,0 +1,7 @@ += Main Document + +Main document's paragraph one, prior to included content. + +include::sub/sub.adoc[leveloffset=+1] + +Main document's paragraph two, after the included content. diff --git a/lib/indir-include-processor/example/sub/images/example.svg b/lib/indir-include-processor/example/sub/images/example.svg new file mode 100644 index 00000000..0d6fb113 --- /dev/null +++ b/lib/indir-include-processor/example/sub/images/example.svg @@ -0,0 +1,15 @@ + + + + + Example Image + + + + + + + + diff --git a/lib/indir-include-processor/example/sub/sub.adoc b/lib/indir-include-processor/example/sub/sub.adoc new file mode 100644 index 00000000..5255ea92 --- /dev/null +++ b/lib/indir-include-processor/example/sub/sub.adoc @@ -0,0 +1,14 @@ += Sub Document + +ifndef::indir[:indir: .] + +Sub document paragraph. + +The variable `indir` has the value `{indir}`. +This variable can be useful for in standalone documents that can also be included into other documents. + +image::{indir}/images/example.svg[] + +The above image has been included using `image::\{indir\}/images/example.svg[]`. +The `indir` variable that is used in the image path always points to the directory of the current (possibly included) asciidoc file. +As a result, the image path is correct, no matter if `sub.adoc` is compiled standalone or included in `master.adoc`. diff --git a/lib/indir-include-processor/extension.rb b/lib/indir-include-processor/extension.rb new file mode 100644 index 00000000..0005808a --- /dev/null +++ b/lib/indir-include-processor/extension.rb @@ -0,0 +1,62 @@ +## +# Asciidoctor extension that adds a variable "indir", pointing at the directory of included asciidoc files. +# +# The indir variable always points at the directory where the current asciidoc file is located. +# The value of the indir variable changes to always reflect the location of the current, included subdocument. +# (Note: This is in contrast to the docfile variable, which remains the same throughout an entire document). +# The indir variable can be used to construct image paths relative to included subdocuments. +# +# Background: +# This extension was created to ease the handling of image paths in nested subdocuments, +# see https://github.com/asciidoctor/asciidoctor/issues/650#issuecomment-433946605. +# +# Motivation: +# The usage scenario that motivates this extension is a nested folder structure with asciidoc files, +# with images stored next to the asciidoc file where they are used. +# For example, an asciidoc file "sub/sub1.adoc" may use an image located at "sub/images/img1.svg". +# In this scenario, we want to be able to compile the asciidoc files in two ways, +# as standalone documents, and included into a parent document. +# The image paths should resolve fine in both cases. +# +# Intended Usage of the Extension: +# +# 1. In the beginning of a subdocument, add this line: +# ifndef::indir[:indir: .] +# +# 2. Include images like this: +# image::{indir}/images/example.svg[] +# +# 3. When compiling a master document (that includes other subdocuments), require this extension. +# The extension will set the indir variable to always point at the directory of the included asciidoc file, +# to that the an image path like "{indir}/images/example.svg" is resolved relative to the included subdocument. +# +# Note that the subdocuments compile just fine without the extension. +# This can be handy to use an editor's built-in preview feature. +# The extension is only needed when compiling a master document (that includes other subdocuments). +# +# Caveats, Future Work: +# This extension, once registered, claims to handle any includes +# (because it does not overwrite the "handles?" method of its parent class, which always return true). +# In consequence, it is difficult to use this extension together with other include processor extensions. +# A better solution with finer-grained control could be based on https://github.com/jirutka/asciidoctor-include-ext. +class IndirIncludeProcessor < Asciidoctor::Extensions::IncludeProcessor + def process doc, reader, target, attributes + content = (open target).readlines + + # Set variables at beginning of the included content + included_docfile = target + included_docdir = ::File.dirname target + content.unshift '' + content.unshift %(:indir: #{included_docdir}) + + # Reset the variables at the end of the included content + parent_docfile = doc.reader.include_stack&.dig(-1, 1) || (doc.attr 'docfile') + parent_docdir = ::File.dirname parent_docfile + content << '' + content << %(:indir: #{parent_docdir}) + + # Push included content for further processing + reader.push_include content, target, target, 1, attributes + reader + end +end