Re: Inserting toc entries from hyperlinked source code

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