class Docbookrx::DocbookVisitor

Constants

ADMONITION_NAMES

COMPLEX_PARA_TAG_NAMES = ['formalpara', 'para']

ANONYMOUS_LITERAL_NAMES
DOCUMENT_NAMES
DocbookNs
ENTITY_TABLE
EOL
EmptyString
FORMATTING_NAMES
FirstLineIndentRx
IGNORED_NAMES
IndentationRx
KEYWORD_NAMES
LIST_NAMES
LITERAL_NAMES
LeadingEndlinesRx
LeadingSpaceRx
NAMED_LITERAL_NAMES
NORMAL_SECTION_NAMES
NextAdjacentChar
OnlyWhitespaceRx
PARA_TAG_NAMES
PATH_NAMES
PrevAdjacentChar
REPLACEMENT_TABLE
SECTION_NAMES
SPECIAL_SECTION_NAMES
TrailingEndlinesRx
UI_NAMES
WrappedIndentRx
XlinkNs
XmlNs

Attributes

lines[R]

Public Class Methods

new(opts = {}) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 82
def initialize opts = {}
  @opts = opts
  @lines = []
  @level = 1
  @skip = {}
  @requires_index = false
  @continuation = false
  @adjoin_next = false
  # QUESTION why not handle idprefix and idseparator as attributes (delete on read)?
  @idprefix = opts[:idprefix] || '_'
  @idseparator = opts[:idseparator] || '_'
  @normalize_ids = opts.fetch :normalize_ids, true
  @compat_mode = opts[:compat_mode]
  @attributes = opts[:attributes] || {}
  @sentence_per_line = opts.fetch :sentence_per_line, true
  @preserve_line_wrap = if @sentence_per_line
    false
  else
    opts.fetch :preserve_line_wrap, true
  end
  @delimit_source = opts.fetch :delimit_source, true
  @list_depth = 0
  @in_table = false
  @nested_formatting = []
  @last_added_was_special = false
  @cwd = opts[:cwd] || Dir.pwd
  @outstanding_callouts = {}
end

Public Instance Methods

adjacent_character(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1625
def adjacent_character node
  if @nested_formatting.length > 1
    true
  elsif ((prev_node = node.previous) && prev_node.type == TEXT_NODE && PrevAdjacentChar =~ prev_node.text) ||
        ((next_node = node.next) && next_node.type == TEXT_NODE && NextAdjacentChar =~ next_node.text)
    true
  elsif (prev_node = node.previous) && ! prev_node.children.empty? && 
        ( FORMATTING_NAMES.include? prev_node.name ) &&
        (adj_child = prev_node.children[0]).type == TEXT_NODE && PrevAdjacentChar =~ adj_child.text
    true
  elsif (next_node = node.next) && (! next_node.children.empty? ) && 
        ( FORMATTING_NAMES.include? next_node.name ) &&
        (adj_child = next_node.children[0]).type == TEXT_NODE && NextAdjacentChar =~ adj_child.text
    true
  elsif (! lines.last.empty?) && (! lines.last.end_with? "\s","\n","\t","\f")
    true
  else
    false
  end
end
after() click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 157
def after
  replace_ifdef_lines
end
after_traverse(node, method) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 335
def after_traverse node, method
  at_root = (node == node.document.root)
  if at_root
    if @requires_index
      append_blank_line
      append_line 'ifdef::backend-docbook[]'
      append_line '[index]'
      append_line '== Index'
      append_line '// Generated automatically by the DocBook toolchain.'
      append_line 'endif::backend-docbook[]'
    end
  else
    method_name = method.to_s
    case method_name
    when "visit_simplelist", "visit_itemizedlist", "visit_orderedlist", "visit_variablelist",
         "visit_procedure", "visit_substeps", "visit_stepalternatives"
      @list_depth -= 1
      append_blank_line if method_name == "visit_variablelist"
    when "visit_table", "visit_informaltable"
      @in_table = false
    when "visit_emphasis", "process_literal"
      @nested_formatting.pop
    end

    @last_added_was_special = false
    case method_name
    when "visit_para", "visit_text", "visit_simpara",
         "visit_emphasis", "visit_link", "visit_xref"
    else
      unless ( FORMATTING_NAMES.include? node.name ) || ( ["uri", "ulink"].include? node.name )
        @last_added_was_special = true
      end
    end
  end

  unless IGNORED_NAMES.include? node.name
    append_ifdef_end_if_condition(node)
  end
end
append_blank_line() click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 261
def append_blank_line
  if @continuation
    @continuation = false
  elsif @adjoin_next
    @adjoin_next = false
  else
    @lines << ''
  end
end
Also aliased as: start_new_line
append_block_role(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 291
def append_block_role node
  process_xml_id node
  if (role = node.attr('role'))
    append_line %([.#{role}])
    #@adjoin_next = true
    true
  else
    false
  end
end
append_block_title(node, prefix = nil) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 272
def append_block_title node, prefix = nil
  if (title_node = (node.at_css '> title') || (node.at_css '> info > title'))
    text = format_text title_node
    title = text.shift(1)[0];
    leading_char = '.'
    # special case for <itemizedlist role="see-also-list"><title>:
    # omit the prefix '.' as we want simple text on a bullet, not a heading
    if node.parent.name == 'itemizedlist' && ((node.attr 'role') == 'see-also-list')
      leading_char = nil
    end
    append_line %(#{leading_char}#{prefix}#{unwrap_text title})
    lines.concat text unless text.empty?
    @adjoin_next = true
    true
  else
    false
  end
end
append_ifdef_end_if_condition(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 2137
def append_ifdef_end_if_condition node
  append_ifdef_if_condition node do |condition|
    append_line "endif::#{condition}[]"
  end
end
append_ifdef_if_condition(node) { |condition| ... } click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 2125
def append_ifdef_if_condition node
  return unless element_with_condition?(node)
  condition = node.attr('condition')
  yield condition
end
append_ifdef_start_if_condition(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 2131
def append_ifdef_start_if_condition node
  append_ifdef_if_condition node do |condition|
    append_line "ifdef::#{condition}[]"
  end
end
append_line(line = '', unsub = false) click to toggle source

Writer methods

# File lib/docbookrx/docbook_visitor.rb, line 240
def append_line line = '', unsub = false
  line = reverse_subs line if !line.empty? && unsub
  @lines << line
end
append_text(text, unsub = false) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 302
def append_text text, unsub = false
  text = reverse_subs text if unsub
  @lines[-1] = %(#{@lines[-1]}#{text})
end
before_traverse(node, method) click to toggle source

Lifecycle callbacks

# File lib/docbookrx/docbook_visitor.rb, line 309
def before_traverse node, method
  at_root = (node == node.document.root)
  if at_root
    title = (text_at_css node, '> title') || (text_at_css node, '> info > title')
    append_line "---"
    append_line "title: \"#{title}\""
    append_line "---"
  end
  unless IGNORED_NAMES.include? node.name
    append_ifdef_start_if_condition(node)
  end

  case method.to_s
  when "visit_simplelist", "visit_itemizedlist", "visit_orderedlist", "visit_variablelist",
       "visit_procedure", "visit_substeps", "visit_stepalternatives"
    @list_depth += 1
  when "visit_table", "visit_informaltable"
    @in_table = true
  when "visit_emphasis"
    marker = get_emphasis_quote_char node
    @nested_formatting.push marker
  when "process_literal"
    @nested_formatting.push '+'
  end
end
cell_prefix(colspecs, cell) click to toggle source

compute prefix for row entry combinings spans and alignments

# File lib/docbookrx/docbook_visitor.rb, line 1339
def cell_prefix colspecs, cell
  align = cell.attribute("align").value rescue nil
  as = case align
         when "left"
           "<"
         when "center"
           "^"
         when "right"
           ">"
         else
           ""
       end
  valign = cell.attribute("valign").value rescue nil
  vas = case valign
          when "top"
            ".<"
          when "middle"
            ".^"
          when "bottom"
            ".>"
          else
            ""
          end
  vspan = entry_vspan(cell)
  vs = vspan ? ".#{vspan+1}" : ""
  hspan = compute_hspan(colspecs, cell)
  hs = (hspan > 1) ? "#{hspan}" : ""
  span = (hs.empty? && vs.empty?) ? "" : "#{hs}#{vs}+"
  "#{span}#{as}#{vas}"
end
choose_screen_tag(node) click to toggle source

process any test inside <screen> check if any child has '—-' in text, switch tag to '.…' in this case return enclosing tag

# File lib/docbookrx/docbook_visitor.rb, line 1118
def choose_screen_tag node
  tag = '----'
  node.children.each do |child|
    text = child.text.strip
    next if text.empty?
    source_lines = text.split EOL
    if source_lines.detect {|line| line.match(/^-{4,}/) }
      append_line '[listing]'
      tag = '....'
      break
    end
  end
  append_line tag
  tag
end
compute_hspan(colspecs, entry) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1321
def compute_hspan colspecs, entry
  nstart, nend = entry_hspan entry
  if nstart && nend
    # if there's a span given, compute the index difference
    sindex = find_colname_index colspecs, nstart
    eindex = find_colname_index colspecs, nend
    if sindex && eindex
      return (eindex - sindex) + 1
    else
      warn %('namest' #{nstart} not found in <colspec>) unless nstart
      warn %('nameend' #{nend} not found in <colspec>) unless nend
    end
  end
  return 1
end
default_visit(node) click to toggle source

Node visitor callbacks

# File lib/docbookrx/docbook_visitor.rb, line 377
def default_visit node
  warn %(No visitor defined for <#{node.name}>! Skipping.)
  false
end
element_with_condition?(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 2121
def element_with_condition? node
  node.type == ELEMENT_NODE && node.attr('condition')
end
entity(number) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 211
def entity number
  [number].pack 'U*'
end
entry_hspan(node) click to toggle source

check for horizontal span of entry return [namest,nameend] of entry

# File lib/docbookrx/docbook_visitor.rb, line 1303
def entry_hspan node
  namest = node.attribute('namest').value rescue nil
  nameend = node.attribute('nameend').value rescue nil
  [namest, nameend]
end
entry_vspan(node) click to toggle source

check for vertical span of entry

# File lib/docbookrx/docbook_visitor.rb, line 1310
def entry_vspan node
  node.attribute("morerows").value.to_i rescue nil
end
find_colname_index(colspecs, name) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1314
def find_colname_index colspecs, name
  colspecs.find_index do |colspec|
    v = colspec.attribute('colname').value rescue nil
    v == name
  end
end
format_append_line(node, suffix="") click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 245
def format_append_line node, suffix=""
  text = format_text node
  line = text.shift(1)[0]
  append_line line + suffix
  lines.concat(text) unless text.empty?
  text
end
format_append_text(node, prefix="", suffix="") click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 253
def format_append_text node, prefix="", suffix=""
  text = format_text node
  line = text.shift(1)[0]
  append_text prefix + line + suffix
  lines.concat(text) unless text.empty?
  text
end
format_text(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 192
def format_text node
  if node && (node.is_a? ::Nokogiri::XML::NodeSet)
    node = node.first
  end

  if node.is_a? ::Nokogiri::XML::Node
    append_blank_line
    last_line = lines.length
    proceed node
    @lines.pop(lines.length-last_line+1)
  else
    nil
  end
end
format_text_at_css(node, css) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 207
def format_text_at_css node, css
  format_text (node.at_css css)
end
generate_id(title) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 628
def generate_id title
  sep = @idseparator
  pre = @idprefix
  # FIXME move regexp to constant
  illegal_sectid_chars = /&(?:[[:alpha:]]+|#[[:digit:]]+|#x[[:alnum:]]+);|\W+?/
  id = %(#{pre}#{title.downcase.gsub(illegal_sectid_chars, sep).tr_s(sep, sep).chomp(sep)})
  if pre.empty? && id.start_with?(sep)
    id = id[1..-1]
    id = id[1..-1] while id.start_with?(sep)
  end
  id
end
get_emphasis_quote_char(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1613
def get_emphasis_quote_char node
  roleAttr = node.attr('role')
  case roleAttr
  when 'strong', 'bold'
    '*'
  when 'marked'
    '#'
  else
    '_'
  end
end
ignore(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 400
def ignore node
  false
end
imagedata_attrs(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1815
def imagedata_attrs node
  imagedata = src = nil
  node.css('imageobject').each do |io|
    id = io.at_css('imagedata')
    src = id.attr('fileref')
    unless src.end_with? '.svg' # prefer .svg files
      next if io.attr('role') == "fo" # skip role=fo
    end
    imagedata = id
    break
  end
  return nil unless imagedata
  width = imagedata.attr('width')
  width_s = (width.nil?) ? "" : "scaledwidth=#{width}"
  alt = text_at_css node, 'textobject phrase'
  generated_alt = ::File.basename(src)[0...-(::File.extname(src).length)]
  alt = nil if alt && alt == generated_alt
  lqa = (lazy_quote alt) || ""
  sep = (lqa.empty? || width_s.empty?) ? "" : ","
  "#{src}[#{lqa}#{sep}#{width_s}]"
end
lazy_quote(text, seek = ',') click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 2109
def lazy_quote text, seek = ','
  if text && (text.include? seek)
    %("#{text}")
  else
    text
  end
end
menu_normalize(str) click to toggle source

replace “…n …” with “… …”

normalize_id(id) click to toggle source

Lowercase id and replace underscores or hyphens with the @idseparator TODO ensure id adheres to @idprefix

# File lib/docbookrx/docbook_visitor.rb, line 651
def normalize_id id
  if id
    normalized_id = id.downcase.tr('_-', @idseparator)
    normalized_id = %(#{@idprefix}#{normalized_id}) if @idprefix && !(normalized_id.start_with? @idprefix)
    normalized_id
  else
    nil
  end
end
proceed(node, opts = {})
Also aliased as: visit_attribution, visit_guiicon
Alias for: traverse_children
process_abstract(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 475
def process_abstract node
  if (abstract_node = (node.at_css '> abstract'))
    append_line
    append_line '[abstract]'
    append_line '--'
    abstract_node.elements.each do |el|
      append_line
      proceed el
      append_line
    end
    append_text '--'
  end
end
process_admonition(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 707
def process_admonition node
  name = node.name
  label = name.upcase
  append_blank_line unless @continuation || (@list_depth > 0)
  have_title = append_block_title node
  if @list_depth > 0
    append_blank_line if have_title
    local_continuation = @continuation
    append_line %(#{label}: )
    @continuation = true
    proceed node
    @continuation = local_continuation
    append_line '+'
    append_blank_line
    append_blank_line
  else
    append_line %([#{label}])
    append_line '===='
    @adjoin_next = true
    proceed node
    @adjoin_next = false
    append_line '===='
  end
  false
end
process_arg_or_group(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1026
def process_arg_or_group node
  choice = node.attr('choice') || 'opt'
  choice = choice.downcase
  rep = node.attr('rep') || 'norepeat'
  rep = rep.downcase
  # Parse the 'choice' attribute
  openchar, closechar = case choice
  when 'opt'
    [ '[ ', ' ]' ]
  when 'req'
    [ '{ ', ' }' ]
  when 'plain'
    [ '', '' ]
  else
    [ '[ ', ' ]' ]
  end
  # Parse the 'rep' attribute
  repeatchar = case rep
  when 'norepeat'
    ''
  when 'repeat'
    '...'
  else
    ''
  end
  separator = ' | '
  append_text ' '
  append_text openchar
  first = true
  node.children.each do |child|
    if (node.name == 'group') && (child.type == ELEMENT_NODE)
      unless first
        append_text separator
      end
      first = false
      child.accept self
    elsif (node.name == 'arg')
      child.accept self
    end
  end
  append_text repeatchar if repeatchar
  append_text closechar
  false
end
process_doc(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 462
def process_doc node
  process_xml_id node
  # In DocBook 5.0, title is directly inside book/article element
  if (title = text_at_css node, '> title')
    append_line %(= #{title})
    append_blank_line
  end
  @level += 1
  proceed node, :using_elements => true
  @level -= 1
  false
end
process_example(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1218
def process_example node
  append_blank_line
  if (id = (resolve_id node, normalize: @normalize_ids))
    append_line %([[#{id}]])
  end
  append_block_title node
  elements = node.elements.to_a
  if elements.size > 0 && elements.first.name == 'title'
    elements.shift
  end
  if elements.size == 1 && (PARA_TAG_NAMES.include? (child = elements.first).name)
    append_line '[example]'
    # must reset adjoin_next in case block title is placed
    @adjoin_next = false
    format_append_line child
  else
    append_line '===='
    @adjoin_next = true
    proceed node
    @adjoin_next = false
    append_line '===='
  end
  false
end
process_info(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 489
def process_info node
  if node.parent != node.document.root
    # In DocBook 4.5, title is nested inside info element
    if (title = text_at_css node, '> title')
      append_line %(= #{title.strip})
    end
    if node.name == 'bookinfo' || node.parent.name == 'book' || node.parent.name == 'chapter'
      append_line ':compat-mode:' if @compat_mode
      append_line ':doctype: book'
      append_line ':sectnums:'
      append_line ':toc: left'
      append_line ':icons: font'
      append_line ':experimental:'
    end
    append_line %(:idprefix: #{@idprefix}).rstrip unless @idprefix == '_'
    append_line %(:idseparator: #{@idseparator}).rstrip unless @idseparator == '_'
    @attributes.each do |name, val|
      append_line %(:#{name}: #{val}).rstrip
    end
  end
  authors = []
  (node.css 'author').each do |author_node|
    # FIXME need to detect DocBook 4.5 vs 5.0 to handle names properly
    author = if (personname_node = (author_node.at_css 'personname'))
      [(text_at_css personname_node, 'firstname'), (text_at_css personname_node, 'surname')].compact * ' '
    else
      [(text_at_css author_node, 'firstname'), (text_at_css author_node, 'surname')].compact * ' '
    end
    if (email_node = (author_node.at_css 'email'))
      author = %(#{author} <#{text email_node}>)
    end
    authors << author unless author.empty?
  end
  append_line (authors * '; ') unless authors.empty?
  date_line = nil
  if (revnumber_node = node.at_css('revhistory revnumber', 'releaseinfo'))
    date_line = %(v#{revnumber_node.text}, ) 
  end
  if (date_node = node.at_css('> date', '> pubdate'))
    append_line %(#{date_line}#{date_node.text})
  end
  process_abstract node
  false
end
process_keyword(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1751
def process_keyword node
  role, char = case (name = node.name)
  when 'firstterm'
    ['term', '_']
  when 'citetitle'
    ['ref', '_']
  else
    [name, '#']
  end
  append_text %([#{role}]#{char}#{node.text}#{char})
  false
end
process_literal(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1764
def process_literal node
  name = node.name
  unless ANONYMOUS_LITERAL_NAMES.include? name
    shortname = case name
    when 'envar'
      'var'
    when 'application'
      'app'
    else
      name.sub 'name', ''
    end
    append_text %([#{shortname}])
  end

  times = (adjacent_character node) ? 2 : 1;
  literal_char = ('`' * times)
  other_format_start = other_format_end = ''

  if @nested_formatting.length > 1 
    emphasis = false
    bold = false
    for i in 0..@nested_formatting.length-2
      case @nested_formatting[i]
      when '_'
        emphasis = true
      when '*'
        bold = true
      end
      if emphasis && bold
        break
      end
    end

    if emphasis && bold
      other_format_start = "**__"
      other_format_end = "__**"
    elsif emphasis
      other_format_start = other_format_end = "__"
    elsif bold
      other_format_start = other_format_end = "**"
    end
  end


  format_append_text node, literal_char + other_format_start, other_format_end + literal_char

  false 
end
process_path(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1668
def process_path node
  case node.name
  when 'systemitem'
    role = node['class'] || node.name
  else
    role = 'path'
  end
  #role = case (name = node.name)
  #when 'directory'
  #  'path'
  #when 'filename'
  #  'path'
  #else
  #  name
  #end
  append_text %([#{role}]``#{node.text}``)
  false
end
process_section(node, special = nil) { || ... } click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 584
def process_section node, special = nil
  append_blank_line
  if special
    append_line ':sectnums!:'
    append_blank_line
    append_line %([#{special}])
  end

  title_node = (node.at_css '> title') || (node.at_css '> info > title')
  title = if title_node
    if (subtitle_node = (node.at_css '> subtitle') || (node.at_css '> info > subtitle'))
      title_node.inner_html += %(: #{subtitle_node.inner_html})
    end
    text = text title_node
  else
    if special
      special.capitalize
    else
      warn %(No title found for section node: #{node})
      'Unknown Title!'
    end
  end
  if (id = (resolve_id node, normalize: @normalize_ids)) && id != (generate_id title)
    append_line %([[#{id}]])
  end
  append_ifdef_start_if_condition(title_node) if title_node
  # title formatting adds spurious \n, strip leading/trailing ones, replace the others with blanks
  t = title.strip.split("\n").map{|s|s.strip}.join(' ')
  append_line %(#{'=' * @level} #{t})
  append_ifdef_end_if_condition(title_node) if title_node
  yield if block_given?
  if (info_node = node.at_css('> info'))
    process_info info_node
  end
  @level += 1
  proceed node, :using_elements => true
  @level -= 1
  if special
    append_blank_line
    append_line ':sectnums:'
  end
  false
end
process_special_section(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 580
def process_special_section node
  process_section node, node.name
end
process_table(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1370
def process_table node
  tgroup = node.at_css '> tgroup'
  numcols = tgroup.attr('cols').to_i
  colspecs = tgroup.css '> colspec'
  head = tgroup.at_css '> thead'
  title = " \'" +
    ((title_node = (node.at_css '> title')).nil? ?
      "" : title_node.children[0].text) +
    "\'"
  unless colspecs.empty? || (colspecs.size == numcols)
    warn %(#{numcols} columns specified in table#{title}, but only #{colspecs.size} colspecs)
  end
  if (head_row = (tgroup.at_css '> thead > row'))
    numheaders = 0
    head_row.css('> entry').each do |entry|
      numheaders += compute_hspan colspecs, entry
    end
    if numheaders != numcols
      warn %(#{numcols} columns specified in table#{title}, but only #{numheaders} headers)
    end
  end
  cols = ('1' * numcols).split('')
  body = tgroup.at_css '> tbody'
  unless body.nil?
    row1 = body.at_css '> row'
    row1_cells = row1.elements
    numcols.times do |i|
      next if (row1_cells[i].nil? || !(element = row1_cells[i].elements.first))
      case element.name
      when 'literallayout'
        cols[i] = %(#{cols[i]}*l)
      end
    end
  end

  if (frame = node.attr('frame'))
    frame = %(, frame="#{frame}")
  else
    frame = nil
  end
  options = []
  if head
    options << 'header'
  end
  if (foot = tgroup.at_css '> tfoot')
    options << 'footer'
  end
  options = (options.empty? ? nil : %(, options="#{options * ','}"))
  append_line %([cols="#{cols * ','}"#{frame}#{options}])
  append_line '|==='
  if head_row
    (head_row.css '> entry').each do |cell|
      pf = cell_prefix colspecs, cell
      append_line %(#{pf}| #{text cell})
    end
    append_blank_line
  end
  (tgroup.css '> tbody > row').each do |row|
    append_ifdef_start_if_condition(row)
    append_blank_line
    row.elements.each do |cell|
      pf = cell_prefix colspecs, cell
      case cell.name
      when 'literallayout'
        append_line "#{pf}|#{text cell}"
      else
        append_line "#{pf}|"
        proceed cell
      end
    end
    append_ifdef_end_if_condition(row)
  end
  if foot
    (foot.css '> row > entry').each do |cell|
      pf = cell_prefix colspecs, cell
      append_line %(#{pf}| #{text cell})
    end
  end
  append_line '|==='
  false
end
process_ui(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1692
def process_ui node
  name = node.name
  if name == 'guilabel' && (next_node = node.next) &&
      next_node.type == ENTITY_REF_NODE && ['rarr', 'gt'].include?(next_node.name)
    name = 'guimenu'
  end

  case name
  # ex. <menuchoice><guimenu>System</guimenu><guisubmenu>Documentation</guisubmenu></menuchoice>
  when 'menuchoice'
    items = node.children.map {|n|
      if (n.type == ELEMENT_NODE) && ['guimenu', 'guisubmenu', 'guimenuitem'].include?(n.name)
        n.instance_variable_set :@skip, true
        n.text
      end
    }.compact
    append_text %(menu:#{items[0]}[#{items[1..-1] * ' > '}])
  # ex. <guimenu>Files</guimenu> (top-level)
  when 'guimenu'
    append_text %(menu:#{menu_normalize(node.text)}[])
    # QUESTION when is this needed??
    #items = []
    #while (node = node.next) && ((node.type == ENTITY_REF_NODE && ['rarr', 'gt'].include?(node.name)) ||
    #  (node.type == ELEMENT_NODE && ['guimenu', 'guilabel'].include?(node.name)))
    #  if node.type == ELEMENT_NODE
    #    items << node.text
    #  end
    #  node.instance_variable_set :@skip, true
    #end
    #append_text %([#{items * ' > '}])
  when 'guibutton'
    append_text %(btn:[#{menu_normalize(node.text)}])
  when 'guilabel'
    append_text %([label]##{node.text}#)
  when 'keycap'
    function = node.attribute("function").value rescue nil
    case function
    when 'control'
      append_text %(kbd:[Ctrl])
    when 'shift'
      append_text %(kbd:[Shift])
    when 'alt'
      append_text %(kbd:[Alt])
    when 'enter'
      append_text %(kbd:[Enter])
    when nil
      # skip
    else
      warn "Unhandled <keycap> function #{function.inspect}"
    end
    unless (t = node.text).empty?
      append_text %(kbd:[#{t}])
    end
  when 'mousebutton'
    append_text %(mouse:[#{node.text}])
  end
  false
end
process_xml_id(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 452
def process_xml_id node
  if (id = (resolve_id node, normalize: @normalize_ids))
    if @lines[-1] == "" && @lines[-2] =~ /\.\s+.+/ # xml_id inside list
      append_text %([[#{id}]])
    else
      append_line %([[#{id}]])
    end
  end
end
replace_ifdef_lines() click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 2143
def replace_ifdef_lines
  out_lines = []
  @lines.each do |line|
    if (data = line.match(/^((ifdef|endif)::.+?\[\])(.+)$/))
      # data[1]: "(ifdef|endif)::something[]"
      out_lines << data[1]
      # data[3]: a string after "[]"
      out_lines << data[3]
    else
      out_lines << line
    end
  end
  @lines = out_lines
end
resolve_id(node, opts = {}) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 641
def resolve_id node, opts = {}
  if (id = node['id'] || node['xml:id'])
    opts[:normalize] ? (normalize_id id) : id
  else
    nil
  end
end
reverse_subs(str) click to toggle source

Replaces XML entities, and other encoded forms that AsciiDoc automatically applies, with their plain-text equivalents.

This method effectively undoes the inline substitutions that AsciiDoc performs.

str - The String to processes

Examples

reverse_subs "&#169; Acme, Inc."
# => "(C) Acme, Inc."

Returns The processed String

# File lib/docbookrx/docbook_visitor.rb, line 228
def reverse_subs str
  ENTITY_TABLE.each do |num, text|
    str = str.gsub((entity num), text)
  end
  REPLACEMENT_TABLE.each do |original, replacement|
    str = str.gsub original, replacement
  end
  str
end
start_new_line()
Alias for: append_blank_line
strip_whitespace(text) click to toggle source

Inline node visitors

# File lib/docbookrx/docbook_visitor.rb, line 1454
def strip_whitespace text
  wsMatch = text.match(OnlyWhitespaceRx)
  if wsMatch != nil && wsMatch.size > 0
    return EmptyString
  end
  res = text.gsub(LeadingEndlinesRx, '')
    .gsub(WrappedIndentRx, @preserve_line_wrap ? EOL : ' ')
    .gsub(TrailingEndlinesRx, '')
  res
end
text(node, unsub = true) click to toggle source

Text extraction and processing methods

# File lib/docbookrx/docbook_visitor.rb, line 170
def text node, unsub = true
  if node
    out = nil;
    if node.is_a? ::Nokogiri::XML::Node
      out = unsub ? reverse_subs(node.text) : node.text
    elsif node.is_a? ::Nokogiri::XML::NodeSet && (first = node.first)
      out = unsub ? reverse_subs(first.text) : first.text
    end
    if ! out.nil? && @in_table
      out.gsub(/\|/, '\|')
    else 
      out
    end
  else
    nil
  end
end
text_at_css(node, css, unsub = true) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 188
def text_at_css node, css, unsub = true
  text (node.at_css css, unsub)
end
traverse_children(node, opts = {}) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 161
def traverse_children node, opts = {}
  (opts[:using_elements] ? node.elements : node.children).each do |child|
    child.accept self
  end
end
Also aliased as: proceed
unwrap_text(text) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 2117
def unwrap_text text
  text.gsub WrappedIndentRx, ''
end
visit(node) click to toggle source

Main processor loop

# File lib/docbookrx/docbook_visitor.rb, line 114
def visit node
  return if node.type == COMMENT_NODE

  name = node.name
  return if @skip[name]
  visit_method_name = case node.type
  when PI_NODE
    :visit_pi
  when DTD_NODE
    :visit_dtd
  when 17
    :visit_entity_decl
  when ENTITY_REF_NODE
    :visit_entity_ref
  else
    if ADMONITION_NAMES.include? name
      :process_admonition
    elsif LITERAL_NAMES.include? name
      :process_literal
    elsif KEYWORD_NAMES.include? name
      :process_keyword
    elsif PATH_NAMES.include? name
      :process_path
    elsif UI_NAMES.include? name
      :process_ui
    elsif NORMAL_SECTION_NAMES.include? name
      :process_section
    elsif SPECIAL_SECTION_NAMES.include? name
      :process_special_section
    else
      %(visit_#{name}).to_sym
    end
  end
  before_traverse node, visit_method_name if (respond_to? :before_traverse)
  result = if respond_to? visit_method_name
    send visit_method_name, node
  elsif respond_to? :default_visit
    send :default_visit, node
  end
  traverse_children node if result == true
  after_traverse node, visit_method_name if (respond_to? :after_traverse)
end
visit_anchor(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1519
def visit_anchor node
  return false if node.parent.name.start_with? 'biblio'
  id = resolve_id node, normalize: @normalize_ids
  append_text %([[#{id}]])
  false
end
visit_arg(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1016
def visit_arg node
  process_arg_or_group node
  false
end
visit_article(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 415
def visit_article node
  process_doc node
end
visit_articleinfo(node)
Alias for: visit_info
visit_attribution(node, opts = {})
Alias for: proceed
visit_bibliodiv(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1075
def visit_bibliodiv node
  append_blank_line
  append_line '[bibliography]'
  true
end
visit_bibliomisc(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1081
def visit_bibliomisc node
  true
end
visit_bibliomixed(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1085
def visit_bibliomixed node
  append_blank_line
  append_text '- '
  node.children.each do |child|
    if child.name == 'abbrev'
      append_text %([[[#{child.text}]]] )
    elsif child.name == 'title'
      append_text child.text
    else
      child.accept self
    end
  end
  false
end
visit_blockquote(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1265
def visit_blockquote node
  append_blank_line
  append_block_title node 
  elements = node.elements.to_a
  # TODO make skipping title a part of append_block_title perhaps?
  if elements.size > 0 && elements.first.name == 'title'
    elements.shift
  end
  if elements.size == 1 && PARA_TAG_NAMES.include?((child = elements.first).name)
    append_line '[quote]'
    format_append_line child
  else
    append_line '____'
    @adjoin_next = true
    proceed node
    @adjoin_next = false
    append_line '____'
  end
  false
end
visit_book(node) click to toggle source

Document node (article | book | chapter) & header node (articleinfo | bookinfo | info) visitors

# File lib/docbookrx/docbook_visitor.rb, line 411
def visit_book node
  process_doc node
end
visit_bookinfo(node)
Alias for: visit_info
visit_bridgehead(node) click to toggle source

Section node visitors

# File lib/docbookrx/docbook_visitor.rb, line 566
def visit_bridgehead node
  level = node.attr('renderas').nil? ? @level : node.attr('renderas').sub('sect', '').to_i + 1
  append_blank_line
  append_line '[float]'
  text = format_text node
  title = text.shift(1)[0];
  if (id = (resolve_id node, normalize: @normalize_ids)) && id != (generate_id title)
    append_line %([[#{id}]])
  end
  append_line %(#{'=' * level} #{unwrap_text title})
  lines.concat text unless text.empty?
  false
end
visit_callout(node, ref=nil) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 2080
def visit_callout node, ref=nil
  unless node.parent.name == "calloutlist"
    warn %(callout outside of calloutlist)
  end
  node.elements.each do |element|
    unless PARA_TAG_NAMES.include? element.name
      warn %(Unhandled <callout> child: <#{element.name}>)
      next
    end
    element.children.each do |child|
      case child.name
      when 'text'
        if ref
          append_line "<#{ref}>#{child.text}"
        else
          append_line child.text
        end
        ref = nil
      else
        visit child
      end
    end
  end
end
visit_calloutlist(node) click to toggle source

<calloutlist><callout arearefs=“…”>…</callout></calloutlist> see github.com/asciidoctor/asciidoctor/issues/1077

# File lib/docbookrx/docbook_visitor.rb, line 2066
def visit_calloutlist node
  node.elements.each do |element|
    unless (element.name == "callout")
      warn %(Expected <callout> after <calloutlist> but got <#{element.name}>. Ignoring.)
      next
    end
    arearefs = element.attribute('arearefs')
    ref = @outstanding_callouts[arearefs.value]
    unless ref
      warn %(<callout> references undefined #{arearefs.value}, ignoring)
    end
    visit_callout element, ref
  end
end
visit_chapter(node, as = :chapter) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 425
def visit_chapter node, as = :chapter
  # treat document with <chapter> root element as books
  if node == node.document.root
    @adjoin_next = true
    process_section node do
      append_line ':compat-mode:' if @compat_mode
      append_line ':doctype: book'
      append_line ':sectnums:'
      append_line ':toc: left'
      append_line ':icons: font'
      append_line ':experimental:'
      append_line %(:idprefix: #{@idprefix}).rstrip unless @idprefix == '_'
      append_line %(:idseparator: #{@idseparator}).rstrip unless @idseparator == '_'
      append_line %(:sourcedir: .) unless @attributes.key? 'sourcedir'
      @attributes.each do |name, val|
        append_line %(:#{name}: #{val}).rstrip
      end
    end
  else
    process_section node
  end
end
visit_citation(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1071
def visit_citation node
  append_text %(<<#{node.text}>>)
end
visit_citerefentry(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 669
def visit_citerefentry node
  return false if node.children.empty?
  first = node.children.first
  refentrytitle = ""
  manvolnum = ""
  node.children.each do |child|
    text = child.text.strip
    case child.name
    when 'refentrytitle'
        refentrytitle = text
    when 'manvolnum'
        manvolnum = text
    end
  end
  if defined? refentrytitle && defined? manvolnum
    append_text " {{< manpage"
    append_text " \"#{refentrytitle}\""
    append_text " \"#{manvolnum}\""
    append_text " >}}"
  end
end
visit_cmdsynopsis(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1011
def visit_cmdsynopsis node
  append_blank_line
  true
end
visit_document(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 382
def visit_document node
  true
end
visit_dtd(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 386
def visit_dtd node
  true
end
visit_email(node) click to toggle source

<email>foo@bar.org</email>

# File lib/docbookrx/docbook_visitor.rb, line 2060
def visit_email node
  append_line "mailto:#{node.text}[<#{node.text}>]"
end
visit_emphasis(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1605
def visit_emphasis node
  quote_char = get_emphasis_quote_char node
  times = (adjacent_character node) ? 2 : 1;

  format_append_text node, (quote_char * times), (quote_char * times)
  false
end
visit_entity_decl(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 390
def visit_entity_decl node
  false
end
visit_entity_ref(node) click to toggle source

Convert XML entity refs into attribute refs - e.g. &prodname; -> {prodname}

# File lib/docbookrx/docbook_visitor.rb, line 395
def visit_entity_ref node
  append_text %({#{node.name}})
  false
end
visit_example(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1210
def visit_example node
  process_example node
end
visit_figure(node) click to toggle source

FIXME share logic w/ visit_inlinemediaobject, which is the same here except no block_title and uses append_text, not append_line

# File lib/docbookrx/docbook_visitor.rb, line 1849
  def visit_figure node
    
    if node.name != 'informalfigure'
      append_blank_line
      append_block_title node
      if id = resolve_id(node, normalize: @normalize_ids)
        append_text %( [[#{id}]])
      end
      append_blank_line
    else
      #if node.name == 'informalfigure'
#      append_block_title node
      append_blank_line
    end
    if (attrs = imagedata_attrs(node))
      output = %(image::#{attrs})
      if node.parent.name == 'listitem'
        append_line output
      else
        append_blank_line
        append_line output
        append_blank_line
      end
    else
      warn %(Unknown mediaobject <#{node.elements.first.name}>! Skipping.)
    end
    false
  end
Also aliased as: visit_informalfigure
visit_footnote(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1879
def visit_footnote node
  append_text %(footnote:[#{(text_at_css node, '> para', '> simpara').strip}])
  # FIXME not sure a blank line is always appropriate
  #append_blank_line
  false
end
visit_foreignphrase(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1595
def visit_foreignphrase node
  format_append_text node
end
visit_formalpara(node) click to toggle source

Block node visitors

# File lib/docbookrx/docbook_visitor.rb, line 663
def visit_formalpara node
  append_blank_line
  append_block_title node
  true
end
visit_funcsynopsis(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1886
def visit_funcsynopsis node
  append_blank_line unless node.parent.name == 'para'
  append_line '[source,c]'
  append_line '----'

  if (info = node.at_xpath 'db:funcsynopsisinfo', 'db': DocbookNs)
    info.text.strip.each_line do |line|
      append_line line.strip
    end
    append_blank_line
  end

  if (prototype = node.at_xpath 'db:funcprototype', 'db': DocbookNs)
    indent = 0
    first = true
    append_blank_line
    if (funcdef = prototype.at_xpath 'db:funcdef', 'db': DocbookNs)
      append_text funcdef.text
      indent = funcdef.text.length + 2
    end

    (prototype.xpath 'db:paramdef', 'db': DocbookNs).each do |paramdef|
      if first
        append_text ' ('
        first = false
      else
        append_text ','
        append_line ' ' * indent
      end
      append_text paramdef.text.sub(/\n.*/m, '')
      if (param = paramdef.at_xpath 'db:funcparams', 'db': DocbookNs)
        append_text %[ (#{param.text})]
      end
    end

    if (varargs = prototype.at_xpath 'db:varargs', 'db': DocbookNs)
      if first
        append_text ' ('
        first = false
      else
        append_text ','
        append_line ' ' * indent
      end
      append_text %(#{varargs.text}...)
    end

    append_text(first ? ' (void);' : ');')
  end

  append_line '----'
end
visit_glossdef(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1006
def visit_glossdef node
  append_line %(  #{text node.elements.first})
  false
end
visit_glossentry(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 993
def visit_glossentry node
  append_blank_line
  if !(previous = node.previous_element) || previous.name != 'glossentry'
    append_line '[glossary]'
  end
  true
end
visit_glossterm(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1001
def visit_glossterm node
  format_append_line node, "::"
  false
end
visit_group(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1021
def visit_group node
  process_arg_or_group node
  false
end
visit_guiicon(node, opts = {})
Alias for: proceed
visit_include(node) click to toggle source

Very rough first pass at processing xi:include

# File lib/docbookrx/docbook_visitor.rb, line 535
def visit_include node
  # QUESTION should we reuse this instance to traverse the new tree?
  href = node.attr 'href'
  include_infile = File.join(@cwd, href)
  include_outfile = include_infile.sub '.xml', '.adoc'
  if ::File.readable? include_infile
    str = ::File.read(include_infile)
    opts = @opts.merge({infile: include_infile})
    dirname = File.dirname(include_infile)
    doc = nil
    Dir.chdir((dirname == ".") ? opts[:cwd] : dirname) do |path|
      doc = Docbookrx.read_xml(str, opts)
    end
    exit 1 unless doc.root
    visitor = self.class.new opts
    doc.root.accept visitor
    result = visitor.lines
    result.shift while result.size > 0 && result.first.empty?
    ::File.open(include_outfile, 'w') {|f| f.write(visitor.lines * EOL) }
  else
    warn %(Include file not readable: #{include_infile})
  end
  append_blank_line
  leveloffset = (@level > 1) ? "leveloffset=#{@level-1}" : ""
  append_line %(include::#{href.sub '.xml', '.adoc'}[#{leveloffset}])
  append_blank_line
  false
end
visit_indexterm(node) click to toggle source

FIXME blank lines showing up between adjacent index terms

# File lib/docbookrx/docbook_visitor.rb, line 1939
def visit_indexterm node
  previous_skipped = false
  if @skip.has_key? :indexterm
    skip_count = @skip[:indexterm]
    if skip_count > 0
      @skip[:indexterm] -= 1
      return false
    else
      @skip.delete :indexterm
      previous_skipped = true
    end
  end

  @requires_index = true
  entries = [(text_at_css node, 'primary'), (text_at_css node, 'secondary'), (text_at_css node, 'tertiary')].compact
  #if previous_skipped && (previous = node.previous_element) && previous.name == 'indexterm'
  #  append_blank_line
  #end
  @skip[:indexterm] = entries.size - 1 if entries.size > 1
  append_blank_line unless @lines[-1].empty?
  append_line %[(((#{entries * ','})))]
  # Only if next word matches the index term do we use double-bracket form
  #if entries.size == 1
  #  append_text %[((#{entries.first}))]
  #else
  #  @skip[:indexterm] = entries.size - 1
  #  append_text %[(((#{entries * ','})))]
  #end
  false
end
visit_info(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 419
def visit_info node
  process_info node if DOCUMENT_NAMES.include? node.parent.name
end
Also aliased as: visit_bookinfo, visit_articleinfo
visit_informalexample(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1214
def visit_informalexample node
  process_example node
end
visit_informalfigure(node)
Alias for: visit_figure
visit_informaltable(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1294
def visit_informaltable node
  append_blank_line
  process_xml_id node
  process_table node
  false
end
visit_inlinemediaobject(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1837
def visit_inlinemediaobject node
  append_text %(image:#{imagedata_attrs node})
  false
end
visit_itemizedlist(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 747
def visit_itemizedlist node
  append_blank_line
  append_block_title node
  append_blank_line if @list_depth == 1
  true
end
visit_keycode(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 2105
def visit_keycode node
  append_text "keycode:[#{node.text}]"
end
visit_keycombo(node) click to toggle source

<keycombo> … <keycap>…</keycap> </keycombo>

# File lib/docbookrx/docbook_visitor.rb, line 2033
def visit_keycombo node
  append_text "kbd:["
  follower = ""
  separator = ""
  appender = "]"
  node.elements.each do |keycap|
    if keycap.name == "keycap"
      if appender.empty?
        append_text "#{follower}kbd:["
        follower = ""
        separator = ""
        appender = "]"
      end
      append_text "#{separator}#{keycap.text}"
    elsif keycap.name == "mousebutton"
      append_text "#{appender}-#{keycap.text}"
      appender = ""
      follower = "-"
    else
      warn %(keycombo not followed by keycap but #{keycap.name.inspect}. Skipping.)
    end
    separator = "+" 
  end
  append_text appender
end
visit_listitem(node) click to toggle source

FIXME this method needs cleanup, remove hardcoded logic!

# File lib/docbookrx/docbook_visitor.rb, line 791
def visit_listitem node
  @adjoin_next = false
  process_xml_id node
  marker = (node.parent.name == 'orderedlist' || node.parent.name == 'procedure' ? '.' * @list_depth : 
    (node.parent.name == 'stepalternatives' ? 'a.' : '*' * @list_depth))
  if @lines[-1].empty?
    append_text marker
  else
    append_line marker
  end

  first_line = true
  unless node.elements.empty?

    only_text = true
    node.children.each do |child|
      if ! ( ( FORMATTING_NAMES.include? child.name ) || ( child.name.eql? "text" ) )
        only_text = false
        break
      end
    end

    if only_text
      text = format_text node
      item_text = text.shift(1)[0]

      item_text.split(EOL).each do |line|
        line = line.gsub IndentationRx, ''
        if line.length > 0
          if first_line
            append_text %( #{line})
          else
            append_line %(  #{line})
          end
        end
      end

      unless text.empty?
        append_line '+'
        lines.concat(text)
      end
    else
      node.children.each_with_index do |child,i|
        if ( child.name.eql? "text" ) && child.text.rstrip.empty?
          next
        end

        local_continuation = false
        unless i == 0 || first_line || (child.name == 'literallayout' || child.name == 'itemizedlist' || child.name == 'orderedlist' || child.name == 'procedure')
          append_line '+' unless lines.last == '+'
          @continuation = true
          local_continuation = true
          first_line = true
        end

        if ( PARA_TAG_NAMES.include? child.name ) || ( child.name.eql? "text" )
          text = format_text child
          item_text = text.shift(1)[0]

          item_text = item_text.sub(/\A\+([^\n])/, "+ \n\\1")
          if item_text.empty? && text.empty?
            next
          end

          item_text.split(EOL).each do |line|
            line = line.gsub IndentationRx, ''
            if line.length > 0
              if first_line
                if local_continuation  # @continuation is reset by format_text
                  append_line %(#{line})
                else
                  append_text %( #{line})
                end
              else
                append_line %(  #{line})
              end
            end
          end

          unless text.empty?
            append_line '+' unless lines.last == "+"
            lines.concat(text)
          end
        else
          if ! FORMATTING_NAMES.include? child.name
            if first_line && ! local_continuation
              append_text ' {empty}' # necessary to fool asciidoctorj into thinking that this is a listitem
            end
            unless local_continuation || (child.name == 'literallayout' || child.name == 'itemizedlist' || child.name == 'orderedlist' || child.name == 'procedure')
              append_line '+'
            end
            @continuation = false
          end
          child.accept self
          @continuation = true
        end
        first_line = false
      end
    end
  else
    text = format_text node
    item_text = text.shift(1)[0]

    item_text.split(EOL).each do |line|
      line = line.gsub IndentationRx, ''
      if line.length > 0
        if first_line
          append_text %( #{line})
          first_line = false
        else
          append_line %(  #{line})
        end
      end
    end

    unless text.empty?
      append_line '+'
      lines.concat(text)
    end
  end
  @continuation = false
  append_blank_line unless lines.last.empty?

  false
end
visit_literallayout(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1100
def visit_literallayout node
  append_blank_line
  source_lines = node.text.rstrip.split EOL
  if (source_lines.detect{|line| line.rstrip.empty?})
    append_line '....'
    append_line node.text.rstrip
    append_line '....'
  else
    source_lines.each do |line|
      append_line %(  #{line})
    end
  end
  false
end
visit_mediaobject(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1842
def visit_mediaobject node
  unless node.attr('role') == "fo" # skip role=fo
    visit_figure node
  end
end
visit_member(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 739
def visit_member node
  append_text "* "
  node.children.each do |child|
    child.accept self
  end
  append_line "" if node.children.last.name == "text"
end
visit_orderedlist(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 769
def visit_orderedlist node
  append_blank_line
  # TODO no title?
  if (numeration = (node.attr 'numeration')) && numeration != 'arabic'
    append_line %([#{numeration}])
  end
  append_blank_line if @list_depth == 1
  true
end
visit_para(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 691
def visit_para node
  empty_last_line = ! lines.empty? && lines.last.empty?
  append_blank_line unless @continuation
  append_block_role node
  append_blank_line unless empty_last_line
  true
end
visit_part(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 448
def visit_part node
  visit_chapter node, :part
end
visit_phrase(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1582
def visit_phrase node
  text = format_text node
  phText = text.shift(1)[0]
  if node.attr 'role'
    # FIXME for now, double up the marks to be sure we catch it
    append_text %([#{node.attr 'role'}]###{phText}##)
  else
    append_text %(#{phText})
  end
  lines.concat(text) unless text.empty?
  false
end
visit_pi(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1970
def visit_pi node
  case node.name
  when 'asciidoc-br'
    append_text ' +'
  when 'asciidoc-hr'
    # <?asciidoc-hr?> will be wrapped in a para/simpara
    append_text '\'' * 3
  end
  false
end
visit_procedure(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 754
def visit_procedure node
  append_blank_line
  process_xml_id node
  append_block_title node, 'Procedure: '
  visit_orderedlist node
end
visit_programlisting(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1185
def visit_programlisting node
  language = node.attr('language') || node.attr('role') || @attributes['source-language']
  language = %(,#{language.downcase}) if language
  linenums = node.attr('linenumbering') == 'numbered'
  append_blank_line unless node.parent.name == 'para'
  append_line %([source#{language}#{linenums ? ',linenums' : nil}])
  if (first_element = node.elements.first) && first_element.name == 'include'
    append_line '----'
    node.elements.each do |el|
      append_line %(include::{sourcedir}/#{el.attr 'href'}[])
    end
    append_line '----'
  else
    source_lines = node.text.rstrip.split EOL
    if @delimit_source || (source_lines.detect {|line| line.rstrip.empty?})
      append_line '----'
      append_line (source_lines * EOL)
      append_line '----'
    else
      append_line (source_lines * EOL)
    end
  end
  false
end
visit_prompt(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1662
def visit_prompt node
  # TODO remove the space left by the prompt
  #@lines.last.chop!
  false
end
visit_qandaset(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1981
def visit_qandaset node
  first = true
  # might be wrapped in 'qandadiv'
  node = node.at_xpath('db:qandadiv', 'db': DocbookNs) || node.at_xpath('qandadiv') || node
  node.elements.to_a.each do |element|
      if element.name == 'title'
        append_line ".#{element.text}"
        append_blank_line
      end
      if first
        append_line '[qanda]'
        first = false
      end
      if element.name == 'qandaentry'
        id = resolve_id element, normalize: @normalize_ids
        question = element.at_xpath('question/para') || element.at_xpath('db:question/db:para', 'db': DocbookNs)
        if (question)
          append_line %([[#{id}]]) if id
          format_append_line question, "::"
          answer = element.at_xpath('answer') || element.at_xpath('db:answer', 'db': DocbookNs)
          if (answer)
            first = true
            answer.children.each_with_index do |child, i|
              unless child.text.rstrip.empty?
                unless first
                  append_line '+'
                  @continuation = true
                end
                first = nil
                child.accept self
              end
            end
            @continuation = false
          else
            warn %(Missing answer in quandaset!)
          end
          append_blank_line
        else
          warn %(Missing question in quandaset! Skipping.)
        end
      end
  end
end
visit_quote(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1601
def visit_quote node
  format_append_text node, '"`', '`"'
end
visit_remark(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1646
def visit_remark node
  append_blank_line
  append_text %(ifdef::showremarks[])
  append_blank_line
  format_append_text node, "#", "#"
  append_blank_line
  append_text %(endif::showremarks[])
  append_blank_line
  false
end
visit_screen(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1134
def visit_screen node
  return false if node.children.empty?
  append_blank_line unless node.parent.name == 'para'
  tag = choose_screen_tag node.children
  first = node.children.first
  node.children.each do |child|
    if child.type == ENTITY_REF_NODE
      s = "{#{child.name}}"
      (child == first) ? append_line(s) : append_text(s)
      next
    end

    text = child.text.strip
    case child.name
    when 'text'
      (child == first) ? append_line(text) : append_text(text)
    when '#cdata-section'
      append_line text
    when 'prompt'
      append_line %(#{text} )
    when 'co' # embedded callout reference
      id = child.attribute_with_ns('id', XmlNs) || child.attribute('id')
      ref = @outstanding_callouts[id.value]
      unless ref
        ref = @outstanding_callouts.size+1
        @outstanding_callouts[id.value] = ref
      end
      append_text " <#{ref}>"
    when 'userinput'
      (child == first) ? append_line("#{text}") : append_text("#{text}")
    when 'replaceable'
      (child == first) ? append_line("`#{text}`") : append_text("`#{text}`")
    when 'comment'
        # skip
    when 'command'
      (child == first) ? append_line("#{text} ") : append_text("#{text} ")
    when 'option'
      (child == first) ? append_line("_#{text}_ ") : append_text("_#{text}_ ")
    when 'xref'
      visit_xref child
    else
      warn %(Cannot handle <#{child.name}> within <screen>)
      child.ancestors.each do |parent|
        warn %(  from #{parent.name})
      end
    end
  end
  append_line tag
  false
end
visit_set(node) click to toggle source

<set> … <xi:include …> </set>

# File lib/docbookrx/docbook_visitor.rb, line 2026
def visit_set node
  node.elements.to_a.each do |element|
    visit element
  end
end
visit_sidebar(node) click to toggle source

FIXME wrap this up in a process_block method

# File lib/docbookrx/docbook_visitor.rb, line 1244
def visit_sidebar node
  append_blank_line
  append_block_title node 
  elements = node.elements.to_a
  # TODO make skipping title a part of append_block_title perhaps?
  if elements.size > 0 && elements.first.name == 'title'
    elements.shift
  end
  if elements.size == 1 && PARA_TAG_NAMES.include?((child = elements.first).name)
    append_line '[sidebar]'
    format_append_line child
  else
    append_line '****'
    @adjoin_next = true
    proceed node
    @adjoin_next = false
    append_line '****'
  end
  false
end
visit_simpara(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 699
def visit_simpara node
  empty_last_line = ! lines.empty? && lines.last.empty?
  append_blank_line
  append_block_role node
  append_blank_line unless empty_last_line
  true
end
visit_simplelist(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 733
def visit_simplelist node
  append_blank_line
  append_block_title node
  append_blank_line if @list_depth == 1
  true
end
visit_step(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 786
def visit_step node
  visit_listitem node
end
visit_stepalternatives(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 765
def visit_stepalternatives node
  visit_orderedlist node
end
visit_substeps(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 761
def visit_substeps node
  visit_orderedlist node
end
visit_table(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1286
def visit_table node
  append_blank_line
  process_xml_id node
  append_block_title node
  process_table node
  false
end
visit_text(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1465
def visit_text node
  in_para = PARA_TAG_NAMES.include?(node.parent.name) || node.parent.name == 'phrase'
  # drop text if empty unless we're processing a paragraph
  unless node.text.rstrip.empty?
    text = node.text
    if in_para
      leading_space_match = text.match LeadingSpaceRx
      # strips surrounding endlines and indentation on normal paragraphs
      # TODO factor out this whitespace processing
      text = strip_whitespace text
      is_first = !node.previous_element
      if is_first
        text = text.lstrip
      elsif leading_space_match && !!(text !~ LeadingSpaceRx)
        if @lines[-1] == "----" || @lines[-1] == "====" 
          text = %(#{leading_space_match[0]}#{text})
        elsif (node_prev = node.previous) &&
              ! ( node_prev.name == "para" || node_prev.name == "text" ) &&
              ( (lines.last.end_with? " ") || (lines.last.end_with? "\n") || lines.last.empty? )
          # no leading space before text
        else
          text = %( #{text})
        end
      end

      # FIXME sentence-per-line logic should be applied at paragraph block level only
      if @sentence_per_line
        # FIXME move regexp to constant
        text = text.gsub(/(?:^|\b)\.[[:blank:]]+(?!\Z)/, %(.#{EOL}))
      end
    end
    # escape |'s in table cell text
    if @in_table
      text = text.gsub(/\|/, '\|')
    end
    if ! @nested_formatting.empty?
      if text.start_with? '_','*','+','`','#'
        text = '\\' + text
      end
    end
    if ( @lines[-1].empty? ) && ( text.start_with? '.' )
      text = text.sub( /\A(\.+)/, "$$\\1$$" )
    end
    if @last_added_was_special
      readd_space = text.end_with? " ","\n"
      text = "\n" + text.rstrip
      text = text + " " if readd_space
    end

    append_text text, true
  end
  false
end
visit_trademark(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1657
def visit_trademark node
  format_append_text node, "", "(TM)"
  false
end
visit_uri(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 1535
def visit_uri node
  url = if node.name == 'ulink'
    node.attr 'url'
  else
    href = (node.attribute_with_ns 'href', XlinkNs)
    if (href)
      href.value
    else
      node.text
    end
  end
  prefix = 'link:'
  if url.start_with?('http://') || url.start_with?('https://')
    prefix = nil
  end
  label = text node
  if label.empty? || url == label
    if (ref = @attributes.key(url))
      url = %({#{ref}})
    end
    append_text %(#{prefix}#{url})
  else
    if (ref = @attributes.key(url))
      url = %({#{ref}})
    end
    append_text %(#{prefix}#{url}[#{label}])
  end
  false
end
Also aliased as: visit_ulink
visit_variablelist(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 779
def visit_variablelist node
  append_blank_line
  append_block_title node
  @lines.pop if @lines[-1].empty?
  true
end
visit_varlistentry(node) click to toggle source
# File lib/docbookrx/docbook_visitor.rb, line 917
  def visit_varlistentry node
    # FIXME adds an extra blank line before first item
    #append_blank_line unless (previous = node.previous_element) && previous.name == 'title'
    append_blank_line
    process_xml_id node
    text = format_text(node.at_css node, '> term')
    text.each do |text_line| 
      text_line.split(EOL).each_with_index do |line,i|
        line = line.gsub IndentationRx, ''
        if line.length > 0
          if i == 0 
            append_line line
          else 
            append_text ( " " + line )
          end
        end
      end
    end
    append_text ":" + (":" * @list_depth)

    first_line = true
    listitem = node.at_css node, '> listitem'
    listitem.elements.each_with_index do |child,i|
      if ( child.name.eql? "text" ) && child.text.rstrip.empty?
        next
      end
      local_continuation = false
      unless i == 0 || first_line || (child.name == 'literallayout' || child.name == 'screen' || (LIST_NAMES.include? child.name) )
        append_line '+'
#        append_line "+#{child.name.inspect}"
        append_blank_line
        @continuation = true
        local_continuation = true
      end
    
      if ( PARA_TAG_NAMES.include? child.name ) || ( child.name.eql? "text" )
        append_blank_line if i == 0

        text = format_text child
        item_text = text.shift(1)[0]
    
        item_text = item_text.sub(/\A\+([^\n])/, "+\n\\1")
        if item_text.empty? && text.empty?
          next
        end
        item_text.split(EOL).each do |line|
          line = line.gsub IndentationRx, ''
          if line.length > 0
            if first_line
              ((line == "----") || (line == "====")) ? (append_line line) : (append_text line)
              first_line = false
            else
              append_line line
            end
          end
        end
    
        unless text.empty?
          append_line '+' unless lines.last == "+"
          lines.concat(text)
        end
      else
        if ! FORMATTING_NAMES.include? child.name
          unless local_continuation || (child.name == 'literallayout' || (LIST_NAMES.include? child.name) )
            append_line '+'
          end
          @continuation = false
        end
        child.accept self
        @continuation = true
      end
    end

    false
  end
visit_xref(node) click to toggle source

QUESTION detect bibliography reference and autogen label?

# File lib/docbookrx/docbook_visitor.rb, line 1568
def visit_xref node
  linkend = node.attr 'linkend'
  id = @normalize_ids ? (normalize_id linkend) : linkend
  text = format_text node
  label = text.shift(1)[0]
  if label.empty?
    append_text %(<<#{id}>>)
  else
    append_text %(<<#{id},#{lazy_quote label}>>)
  end
  lines.concat(text) unless text.empty?
  false
end