2
0
mirror of https://github.com/boostorg/json.git synced 2026-01-19 04:12:14 +00:00
Files
json/doc/htmldir.rb
2025-06-18 15:12:04 +03:00

459 lines
14 KiB
Ruby

# coding: utf-8
require 'asciidoctor'
require 'asciidoctor/converter/html5'
require 'pathname'
class Asciidoctor::AbstractBlock
attr_accessor :real_level
def document_dirname
if @document.upper_doc && (@document.upper_doc != @document.top_doc)
base = @document.upper_doc.document_filepath()
base.delete_suffix @document.attributes['outfilesuffix']
else
"."
end
end
def document_filename
base = @document.id
if @document.upper_doc && (@document.upper_doc != @document.top_doc)
base = base.delete_prefix "#{document.upper_doc.id}_"
end
base + @document.attributes['outfilesuffix']
end
def document_filepath
suffix = @document.attributes['outfilesuffix']
File.join(document_dirname, @document.document_filename).delete_prefix(
".#{File::SEPARATOR}")
end
def filepath_to other
self_dir = Pathname.new document_dirname
other_dir = Pathname.new other.document_dirname
ret = File.join(
other_dir.relative_path_from(self_dir),
other.document_filename).delete_prefix(".#{File::SEPARATOR}")
ret
end
end
class Asciidoctor::Document
attr_accessor :parsed
attr_accessor :processed
attr_accessor :catalog
attr_accessor :child_documents
attr_accessor :top_doc
attr_accessor :upper_doc
attr_accessor :next_doc
attr_accessor :prev_doc
alias core_init initialize
def initialize data = nil, options = {}
options[:sourcemap] = true
core_init data, options
@child_documents = []
end
def content # this is different from the reguar Document.content
super
end
alias core_docinfo docinfo
def docinfo location = :head, suffix = nil
result = core_docinfo location, suffix
if ((location == :footer) &&
(attr? 'source-location', nil, true) &&
(attr? 'project-gh', nil, true))
result += '<div class="edit-link"><div class="edit-link-content">'
result += %(<a href="#{attr 'project-gh'}/#{attr 'source-location'})
result += '">Edit this page</a></div></div>'
end
if [:header, :footer].include?(location)
result += '<div class="nav-links"><div class="nav-links-content">'
if location == :header
if self.upper_doc
links = []
ancestor = self
while ancestor
links.prepend(
nav_link_html(ancestor, 'nav-link nav-crumb'))
ancestor = ancestor.upper_doc
end
result +=
%(<nav id="breadcrumbs"><ul>#{links.join}</ul></nav>)
end
block_id = 'siblings-header'
else # location == :footer
block_id = 'siblings-footer'
end
links = []
if @prev_doc
links << nav_link_html(@prev_doc, 'nav-link nav-prev')
end
if @next_doc
links << nav_link_html(@next_doc, 'nav-link nav-next')
end
result += %(<nav id="#{block_id}"><ul>#{links.join}</ul></nav>)
result += '</div></div>'
end
result
end
def link_to_target(refid, target = "##{refid}")
doc = @catalog[:refs][refid]
until !doc || (doc.context == :document)
doc = doc.parent
end
if !doc || (doc.id == @id)
target
elsif "##{doc.id}" == target
@document.filepath_to doc
else
"#{@document.filepath_to doc}#{target}"
end
end
def nav_link_html(target, role)
text = target.captioned_title
title = target.document.doctitle sanitize: true
text = %(<span class="title">#{text}</span>)
link = link_to_target target.id
link = %(<a href="#{link}" title="#{title}">#{text}</a>)
%(<li class="#{role}">#{link}</li>)
end
def spawn_child_documents
pagelevels = attr('pagelevels', 0).to_i
if pagelevels < 0
logger.warn "attribute 'pagelevels' must be >= 0; setting to 0"
pagelevels = 0
end
set_attribute('pagelevels', pagelevels.to_s)
@id = File.basename(
@attributes['outfile'] , @attributes['outfilesuffix'])
register(
:refs,
[@id,
Asciidoctor::Inline.new(
parent = self,
context = :anchor,
text = doctitle,
opts = {:type => :ref, :id => @id}),
doctitle])
@child_documents = generate_child_docs self, pagelevels
@real_level = 0
if sections.empty?
attrs = @attributes.clone
@blocks << Asciidoctor::Section.new(
self,
1,
false,
{:attributes => attrs})
end
reindex_sections
update_source_location
@top_doc = self
make_linear_connections
update_real_level self, 0
@processed = true
end
def generate_child_docs(node, doc_pagelevels)
result = []
node.blocks.filter! do |block|
if block.context != :section
next true
end
node.document.playback_attributes block.attributes
pagelevels = block.attr('pagelevels', doc_pagelevels).to_i
if block.level > pagelevels || !block.id
next true
end
result << node.document.spawn_document_from(block, doc_pagelevels)
false
end
result
end
def spawn_document_from(node, doc_pagelevels)
pagelevels = node.attr('pagelevels', doc_pagelevels).to_i
attrs = @attributes.clone
attrs['title'] = node.title
attrs['notitle'] = true
attrs['authors'] = nil
attrs['author'] = nil
attrs['pagelevels'] = doc_pagelevels
doc = Asciidoctor::Document.new(
[],
{:attributes => attrs,
:doctype => self.doctype,
:header_footer => !self.attr?(:embedded),
:safe => self.safe})
doc.id = node.id
doc.catalog = @catalog
doc.child_documents = generate_child_docs(node, pagelevels)
doc.blocks << node
doc.finalize_header({})
reparent_blocks(node, doc)
doc.real_level = node.real_level
doc.reindex_sections
doc.update_source_location
doc.parsed = true
doc.processed = true
doc
end
def update_real_level(node, real_level)
node.real_level = real_level
if node.context == :dlist
node.find_by(context: :list_item).each do |block|
update_real_level block, real_level + 1
end
else
node.blocks.each do |block|
update_real_level block, real_level + 1
end
end
end
def reparent_blocks(node, parent, real_level = node.level)
node.parent = parent
node.real_level = real_level
node.level = node.level - real_level + 1
if node.context == :dlist
node.find_by(context: :list_item).each do |block|
reparent_blocks(block, node, real_level + 1)
end
return
end
node.blocks.each do |block|
reparent_blocks(block, node, real_level + 1)
if block.context == :table
block.columns.each do |col|
col.parent = col.parent
end
block.rows.body.each do |row|
row.each do |cell|
cell.parent = cell.parent
end
end
end
end
end
def make_linear_connections
prev = self
child_documents.each do |doc|
doc.top_doc = @top_doc
doc.upper_doc = self
doc.prev_doc = prev
prev.next_doc = doc
prev = doc.make_linear_connections
end
prev
end
def update_source_location
if (first_section.attr? 'source-location', nil, true)
set_attribute(
'source-location', first_section.attr('source-location'))
return
end
if !first_section.source_location
return
end
path = Pathname.new first_section.source_location.file
if (attr? 'project-dir', nil, true)
path = path.relative_path_from(attr 'project-dir')
end
set_attribute('source-location', path.to_s)
end
end
class HtmlDirConverter < Asciidoctor::Converter::Html5Converter
include Asciidoctor
include Asciidoctor::Converter
include Asciidoctor::Writer
register_for 'htmldir'
attr_accessor :home_doc
def convert_document(node)
if !node.processed
@home_doc = node
node.spawn_child_documents
end
return super
end
def convert_outline(node, opts = {})
convert_outline_doc node.document, node.top_doc, opts
end
def convert_outline_doc(doc, node, opts)
toclevels = ((node.first_section.attr 'toclevels') ||
opts[:toclevels] || (node.document.attr 'toclevels', 2)).to_i
return unless node.real_level < toclevels
opts[:toclevels] = toclevels
if node.first_section.id == node.id
sections = node.first_section.sections
else
sections = node.sections
end
children = sections + node.child_documents
convert_outline_ext doc, children, node.real_level + 1, opts
end
def convert_outline_sect(doc, node, opts)
toclevels = ((node.attr 'toclevels').to_i || opts[:toclevels] || 2)
return unless node.real_level < toclevels
opts[:toclevels] = toclevels
convert_outline_ext doc, node.sections, node.real_level + 1, opts
end
def convert_outline_ext(doc, nodes, level, opts)
return unless !nodes.empty?
sectnumlevels = (
opts[:sectnumlevels] ||
(nodes[0].document.attributes['sectnumlevels'] || 3).to_i)
result = [%(<ul class="sectlevel#{level}">)]
nodes.each do |node|
result << list_item_for_node(doc, node, sectnumlevels)
if node.context == :document
child_toc_level = convert_outline_doc doc, node, opts
else
child_toc_level = convert_outline_sect doc, node, opts
end
result << child_toc_level if child_toc_level
end
result << '</ul>'
result.join LF
end
def list_item_for_node(doc, node, sectnumlevels)
if node.context == :document
section = node.sections[0]
else
section = node
end
slevel = section.real_level
signifier = nil
sectnum = nil
if !section.caption && (
section.numbered && slevel <= sectnumlevels)
if slevel < 2 && node.document.doctype == 'book'
case section.sectname
when 'chapter'
signifier = (
node.document.attributes['chapter-signifier'])
sectnum = section.sectnum
when 'part'
signifier = (
node.document.attributes['part-signifier'])
sectnum = section.sectnum nil, ':'
else
sectnum = section.sectnum
end
end
end
signifier = signifier ? "#{signifier} " : ''
sectnum = sectnum ? "#{sectnum} " : ''
stitle = %(#{signifier}#{sectnum}#{section.title})
stitle = stitle.gsub DropAnchorRx, '' if stitle.include? '<a'
if section.id == doc.id
role = ' class="this-page"'
target = "#"
else
role = ""
target = doc.link_to_target section.id
end
%(<li><a href="#{target}"#{role}>#{stitle}</a>)
end
def convert_inline_anchor(node)
if node.type != :xref
return super
end
if (path = node.attributes['path'])
attrs = append_link_constraint_attrs(
node, node.role ? [%( class="#{node.role}")] : [])
attrs = attrs.join
text = node.text || path
else
attrs = node.role ? %( class="#{node.role}") : ''
unless (text = node.text)
ref = (@refs ||= node.document.catalog[:refs])[
refid = node.attributes['refid']] ||
(refid.nil_or_empty? ? (top = get_root_document node) : nil)
if AbstractNode === ref
if (@resolving_xref ||= (outer = true)) && outer
if (text = ref.xreftext node.attr 'xrefstyle', nil, true)
text = text.gsub DropAnchorRx, '' if text.include? '<a'
else
text = top ? '[^top]' : %([#{refid}])
end
@resolving_xref = nil
else
text = top ? '[^top]' : %([#{refid}])
end
else
text = %([#{refid}])
end
end
end
target = node.document.link_to_target(
node.attributes['refid'], node.target)
%(<a href="#{target}"#{attrs}>#{text}</a>)
end
def write output, target
File.write target, output, mode: Asciidoctor::FILE_WRITE_MODE
write_children(
@home_doc, File.dirname(target), File.extname(target))
end
def write_children(doc, outdir, ext)
doc.child_documents.each do |child|
outfile = File.join(outdir, child.document_filepath)
outfiledir = File.dirname(outfile)
FileUtils.mkdir_p(outfiledir) unless File.exist?(outfiledir)
File.open(outfile, 'w') do |f|
logger.info %(Writing to '#{outfile}')
f.write(child.convert)
end
write_children(child, outdir, ext)
end
end
end