Class: Asciidoctor::InterdocReftext::Processor
- Inherits:
-
Extensions::TreeProcessor
- Object
- Extensions::TreeProcessor
- Asciidoctor::InterdocReftext::Processor
- Defined in:
- lib/asciidoctor/interdoc_reftext/processor.rb
Overview
Asciidoctor processor that adds support for automatic cross-reference text for inter-document cross references.
Implementation Considerations
Asciidoctor does not allow to cleanly change the way of resolving
xreftext for xref:path#[]
macro with path and without explicit xreflabel;
it always uses path as the default xreflabel.
xref:[]
macros are parsed and even converted inAsciidoctor::Substitutors#sub_inline_xrefs
- a single, huge and nasty method that accepts a text (e.g. whole paragraph) and returns the text with convertedxref:[]
macros. The conversion is delegated toAsciidoctor::Inline#convert
- for each macro a new instance ofInline
node is created and then#convert
is called.Inline#convert
just callsconverter.convert
withself
, i.e. it's dispatched to converter'sinline_anchor
handler.The built-in so called HTML5 converter looks into the catalog of references (
document.catalog[:refs]
) for reflabel for the xref's refid, but only if xref node does not define attribute path or text (explicit reflabel). If text is not set and path is set, i.e. it's an inter-document reference without explicit reflabel, catalog of references is bypassed and path is used as a reflabel.
Eh, this is really nasty... The least evil way how to achieve the goal
seems to be monkey-patching of the Asciidoctor::Inline
class. This is
done via InlineNodeMixin which is prepended* into the Inline
class on
initialization of this processor.
The actual logic that resolves reflabel for the given refid is implemented in class Resolver. The Processor is responsible for creating an instance of Resolver for the processed document and injecting it into instance variable RESOLVER_VAR_NAME in the document, so InlineNodeMixin can access it.
Prepending* InlineNodeMixin into the Asciidoctor::Inline
class has
(obviously) a global effect. However, if RESOLVER_VAR_NAME is not
injected in the document object (e.g. extension is not active), Inline
behaves the same as without InlineNodeMixin.
_* If running under Opal (JavaScript), InlineNodeMixin is not prepended
into the Asciidoctor::Inline
, because Opal does not support that. Thus
it's included and the #text
method is overriden using poor alias method
chain approach.
NOTE: We use reftext and reflabel as interchangeable terms in this gem.
Constant Summary collapse
- RESOLVER_VAR_NAME =
Name of instance variable that is dynamically defined in a document object; it contains an instance of the Resolver for the document.
:@_interdoc_reftext_resolver
Instance Method Summary collapse
-
#initialize(resolver_class: Resolver, **resolver_opts) ⇒ Processor
constructor
A new instance of Processor.
- #process(document) ⇒ void
Constructor Details
#initialize(resolver_class: Resolver, **resolver_opts) ⇒ Processor
Returns a new instance of Processor.
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/asciidoctor/interdoc_reftext/processor.rb', line 66 def initialize(resolver_class: Resolver, **resolver_opts) super @resolver_class = resolver_class @resolver_opts = resolver_opts # Workaround for a bug in Ruby 3.0 (see #6). @resolver_opts.delete(:resolver_class) # Monkey-patch Asciidoctor::Inline unless already patched. unless ::Asciidoctor::Inline.include? InlineNodeMixin if RUBY_PLATFORM == 'opal' # Opal does not support `Module#prepend`, so we have to fallback to # `include` with poor alias method chain approach. ::Asciidoctor::Inline.send(:include, InlineNodeMixin) else ::Asciidoctor::Inline.send(:prepend, InlineNodeMixin) end end end |
Instance Method Details
#process(document) ⇒ void
86 87 88 89 90 |
# File 'lib/asciidoctor/interdoc_reftext/processor.rb', line 86 def process(document) resolver = @resolver_class.new(document, **@resolver_opts) document.instance_variable_set(RESOLVER_VAR_NAME, resolver) nil end |