Posted by
daveb on
URL: https://discuss.asciidoctor.org/Inserting-toc-entries-from-hyperlinked-source-code-tp668p707.html
Dan, that did the trick. I now have my extensions working and if anyone is interested they are below. I have updated to toc with a postprocessor extension which is a little fragile and could break if the toc markup changed. It would seem reasonable for a block macro to be able to add entries into the toc directly (maybe it can and I just didn't find out how).
Dave.
require 'asciidoctor'
require 'asciidoctor/extensions'
require 'coderay'
# Easy, if clunky, way of passing data between extensions.
$headings = {}
Asciidoctor::Extensions.register do |document|
block_macro :bcode, BSourceCode
postprocessor CustomTocPostprocessor
end
# Find all the function definitions in the target file and after it has been syntax
# highlighted make the functions hyperlink targets. Make all calls to these functions
# hyperlinks and store the function names in a global for later use.
class BSourceCode < Asciidoctor::Extensions::BlockMacroProcessor
def process (parent, target, attributes)
src_dir = @document.attributes.fetch('src_dir', '')
src_file = File.join(src_dir, target)
css = @document.attributes.fetch('coderay-css','class').to_sym
source = CodeRay::Duo.new(:c, :html, :css => css).highlight(File.read(src_file))
functions = []
prefix = File.basename(target)
# Find all the function definitions and add anchors to them.
source.gsub!(/(
.*?<\/span>\s*)(\w+)/) do
functions << $2
$1 + "#{$2}"
end
# Find all calls to the functions and make them into hyperlinks.
source.gsub!(/(?<![_>])(#{functions.join('|')})/, '<a href="#' + prefix + '_\1">\1')
content = %Q{
<div class="listingblock">
<div class="content">
<pre class="CodeRay"><code class="c language-c">
#{source}
</div>
</div>
}
# Record the headings we want to add into the toc.
$headings[parent.name] = {:prefix => prefix, :functions => functions}
Asciidoctor::Block.new parent, :pass, :content_model => :raw, :source => content
end
end
# Add all the functions we have hyperlinked into the toc.
class CustomTocPostprocessor < Asciidoctor::Extensions::Postprocessor
def process(output)
$headings.each do |name, headings|
prefix = headings[:prefix]
section = %Q{<ul class="sectlevel2">\n} +
headings[:functions].map {|f| %Q{<li><a href="##{prefix}_#{f}">#{f}<\/a><\/li>\n}}.join
output = output.sub(/^(<li><a href=".*?#{name}<\/a><\/li>)$/, '\1' + section)
end
output
end
end
def asciidoctor_runner (file, attributes = {})
attributes.merge!( 'doctype' => 'book',
'source-highlighter' => 'coderay',
'data-uri' => true,
'toc2' => true,
'toclevels' => 4,
'numbered' => true,
'linkcss' => true
)
Asciidoctor.render_file(file, :in_place => true,
:backend => 'html',
:safe => :unsafe, # just allow access to any dir for includes
:attributes => attributes)
end