lib/asciidoctor/block.rb in asciidoctor-0.0.1 vs lib/asciidoctor/block.rb in asciidoctor-0.0.2
- old
+ new
@@ -1,10 +1,10 @@
# Public: Methods for managing blocks of Asciidoc content in a section.
#
# Examples
#
-# block = Asciidoctor::Block.new(:paragraph, ["`This` is a <test>"])
+# block = Asciidoctor::Block.new(document, :paragraph, ["`This` is a <test>"])
# block.content
# => ["<em>This</em> is a <test>"]
class Asciidoctor::Block
# Public: Get the Symbol context for this section block.
attr_reader :context
@@ -16,10 +16,14 @@
attr_accessor :buffer
# Public: Get/Set the String section anchor name.
attr_accessor :anchor
+ # Public: Get/Set the Integer block level (for nested elements, like
+ # list elements).
+ attr_accessor :level
+
# Public: Get/Set the String block title.
attr_accessor :title
# Public: Get/Set the String block caption.
attr_accessor :caption
@@ -27,27 +31,32 @@
# Public: Initialize an Asciidoctor::Block object.
#
# parent - The parent Asciidoc Object.
# context - The Symbol context name for the type of content.
# buffer - The Array buffer of source data.
+
+ # TODO: Don't really need the parent, just the document (for access
+ # both to its renderer, as well as its references and other defined
+ # elements). Would probably be better to pass in just the document.
def initialize(parent, context, buffer=nil)
@parent = parent
@context = context
@buffer = buffer
@blocks = []
end
# Public: Get the Asciidoctor::Document instance to which this Block belongs
def document
- @parent.is_a?(Asciidoctor::Document) ? @parent : @parent.document
+ return @document if @document
+ @document = (@parent.is_a?(Asciidoctor::Document) ? @parent : @parent.document)
end
# Public: Get the Asciidoctor::Renderer instance being used for the ancestor
# Asciidoctor::Document instance.
def renderer
- @parent.renderer
+ document.renderer
end
# Public: Get the rendered String content for this Block. If the block
# has child blocks, the content method should cause them to be
# rendered and returned as content that can be included in the
@@ -57,25 +66,73 @@
Asciidoctor.debug "Parent is #{@parent}"
Asciidoctor.debug "Renderer is #{renderer}"
renderer.render("section_#{context}", self)
end
+ def splain(parent_level = 0)
+ parent_level += 1
+ Asciidoctor.puts_indented(parent_level, "Block title: #{title}") unless self.title.nil?
+ Asciidoctor.puts_indented(parent_level, "Block anchor: #{anchor}") unless self.anchor.nil?
+ Asciidoctor.puts_indented(parent_level, "Block caption: #{caption}") unless self.caption.nil?
+ Asciidoctor.puts_indented(parent_level, "Block level: #{level}") unless self.level.nil?
+ Asciidoctor.puts_indented(parent_level, "Block context: #{context}") unless self.context.nil?
+
+ Asciidoctor.puts_indented(parent_level, "Blocks: #{@blocks.count}")
+
+ if buffer.is_a? Enumerable
+ buffer.each_with_index do |buf, i|
+ Asciidoctor.puts_indented(parent_level, "v" * (60 - parent_level*2))
+ Asciidoctor.puts_indented(parent_level, "Buffer ##{i} is a #{buf.class}")
+ Asciidoctor.puts_indented(parent_level, "Name is #{buf.name rescue 'n/a'}")
+
+ if buf.respond_to? :splain
+ buf.splain(parent_level)
+ else
+ Asciidoctor.puts_indented(parent_level, "Buffer: #{buf}")
+ end
+ Asciidoctor.puts_indented(parent_level, "^" * (60 - parent_level*2))
+ end
+ else
+ if buffer.respond_to? :splain
+ buffer.splain(parent_level)
+ else
+ Asciidoctor.puts_indented(parent_level, "Buffer: #{@buffer}")
+ end
+ end
+
+ @blocks.each_with_index do |block, i|
+ Asciidoctor.puts_indented(parent_level, "v" * (60 - parent_level*2))
+ Asciidoctor.puts_indented(parent_level, "Block ##{i} is a #{block.class}")
+ Asciidoctor.puts_indented(parent_level, "Name is #{block.name rescue 'n/a'}")
+
+ block.splain(parent_level) if block.respond_to? :splain
+ Asciidoctor.puts_indented(parent_level, "^" * (60 - parent_level*2))
+ end
+ nil
+ end
+
# Public: Get an HTML-ified version of the source buffer, with special
# Asciidoc characters and entities converted to their HTML equivalents.
#
# Examples
#
- # block = Asciidoctor::Block.new(:paragraph, ['`This` is what happens when you <meet> a stranger in the <alps>!'])
+ # doc = Asciidoctor::Document.new([])
+ # block = Asciidoctor::Block.new(doc, :paragraph,
+ # ['`This` is what happens when you <meet> a stranger in the <alps>!'])
# block.content
# => ["<em>This</em> is what happens when you <meet> a stranger in the <alps>!"]
#
# TODO:
- # * forced line breaks
+ # * forced line breaks (partly done, at least in regular paragraphs)
# * bold, mono
# * double/single quotes
# * super/sub script
def content
+
+ Asciidoctor.debug "For the record, buffer is:"
+ Asciidoctor.debug @buffer.inspect
+
case @context
when :dlist
@buffer.map do |dt, dd|
if !dt.anchor.nil? && !dt.anchor.empty?
html_dt = "<a id=#{dt.anchor}></a>" + htmlify(dt.content)
@@ -91,25 +148,110 @@
[html_dt, html_dd]
end
when :oblock, :quote
blocks.map{|block| block.render}.join
- when :olist, :ulist, :colist
+ when :olist, :colist
@buffer.map do |li|
htmlify(li.content) + li.blocks.map{|block| block.render}.join
end
+ when :ulist
+ @buffer.map do |element|
+ if element.is_a? Asciidoctor::ListItem
+ element.content = sub_attributes(element.content)
+ end
+ # TODO - not sure why tests work the same whether or not this is commented out.
+ # I think that I am likely not yet testing unordered list items with no block
+ # content. Still and all, it seems like this should be all done by list_item.render .
+ element.render # + element.blocks.map{|block| block.render}.join
+ end
when :listing
@buffer.map{|l| CGI.escapeHTML(l).gsub(/(<\d+>)/,'<b>\1</b>')}.join
when :literal
htmlify( @buffer.join.gsub( '*', '{asterisk}' ).gsub( '\'', '{apostrophe}' ))
when :verse
- htmlify( @buffer.map{ |l| l.strip }.join( "\n" ) )
+ htmlify( sub_attributes(@buffer).map{ |l| l.strip }.join( "\n" ) )
else
- htmlify( @buffer.map{ |l| l.lstrip }.join )
+ lines = sub_attributes(@buffer).map do |line|
+ line.strip
+ line.gsub(Asciidoctor::REGEXP[:line_break], '\1{br-asciidoctor}')
+ end
+ lines = htmlify( lines.join )
+ sub_html_attributes(lines) # got to clean up the br-asciidoctor line-break
end
end
+ # Attribute substitution
+ #
+ # TODO: Tom all the docs
+ def sub_attributes(lines)
+ Asciidoctor.debug "Entering #{__method__} from #{caller[0]}"
+ if lines.is_a? String
+ return_string = true
+ lines = Array(lines)
+ end
+
+ result = lines.map do |line|
+ Asciidoctor.debug "#{__method__} -> Processing line: #{line}"
+ # gsub! doesn't have lookbehind, so we have to capture and re-insert
+ f = line.gsub(/ (^|[^\\]) \{ (\w[\w\-_]+\w) \} /x) do
+ if self.document.defines.has_key?($2)
+ # Substitute from user defines first
+ $1 + self.document.defines[$2]
+ elsif Asciidoctor::INTRINSICS.has_key?($2)
+ # Then do intrinsics
+ $1 + Asciidoctor::INTRINSICS[$2]
+ elsif Asciidoctor::HTML_ELEMENTS.has_key?($2)
+ $1 + Asciidoctor::HTML_ELEMENTS[$2]
+ else
+ Asciidoctor.debug "Bailing on key: #{$2}"
+ # delete the line if it has a bad attribute
+ # TODO: According to AsciiDoc, we're supposed to delete any line
+ # containing a bad attribute. Eek! Can't do that here via gsub!.
+ # (See `subs_attrs` function in asciidoc.py for many gory details.)
+ "{ZZZZZ}"
+ end
+ end
+ Asciidoctor.debug "#{__method__} -> Processed line: #{f}"
+ f
+ end
+ Asciidoctor.debug "#{__method__} -> result looks like #{result.inspect}"
+ result.reject! {|l| l =~ /\{ZZZZZ\}/}
+
+ if return_string
+ result = result.join
+ end
+ result
+ end
+
+ def sub_html_attributes(lines)
+ Asciidoctor.debug "Entering #{__method__} from #{caller[0]}"
+ if lines.is_a? String
+ return_string = true
+ lines = Array(lines)
+ end
+
+ result = lines.map do |line|
+ Asciidoctor.debug "#{__method__} -> Processing line: #{line}"
+ # gsub! doesn't have lookbehind, so we have to capture and re-insert
+ line.gsub(/ (^|[^\\]) \{ (\w[\w\-_]+\w) \} /x) do
+ if Asciidoctor::HTML_ELEMENTS.has_key?($2)
+ $1 + Asciidoctor::HTML_ELEMENTS[$2]
+ else
+ $1 + "{#{$2}}"
+ end
+ end
+ end
+ Asciidoctor.debug "#{__method__} -> result looks like #{result.inspect}"
+ result.reject! {|l| l =~ /\{ZZZZZ\}/}
+
+ if return_string
+ result = result.join
+ end
+ result
+ end
+
private
# Private: Return a String HTML version of the source string, with
# Asciidoc characters converted and HTML entities escaped.
#
@@ -141,12 +283,20 @@
end
html = CGI.escapeHTML(html)
html.gsub!(Asciidoctor::REGEXP[:biblio], '<a name="\1">[\1]</a>')
html.gsub!(Asciidoctor::REGEXP[:ruler], '<hr>\n')
- html.gsub!(/``(.*?)''/m, '“\1”')
- html.gsub!(/`(.*?)'/m, '‘\1’')
+ html.gsub!(/``([^`']*)''/m, '“\1”')
+ html.gsub!(/(?:\s|^)`([^`']*)'/m, '‘\1’')
+
+ # TODO: This text thus quoted is supposed to be rendered as an
+ # "inline literal passthrough", meaning that it is rendered
+ # in a monospace font, but also doesn't go through any further
+ # text substitution, except for special character substitution.
+ # So we need to technically pull this text out, sha it and store
+ # a marker and replace it after the other gsub!s are done in here.
+ # See: http://www.methods.co.nz/asciidoc/userguide.html#X80
html.gsub!(/`([^`]+)`/m) { "<tt>#{$1.gsub( '*', '{asterisk}' ).gsub( '\'', '{apostrophe}' )}</tt>" }
html.gsub!(/([\s\W])#(.+?)#([\s\W])/, '\1\2\3')
# "Unconstrained" quotes
html.gsub!(/\_\_([^\_]+)\_\_/m, '<em>\1</em>')
@@ -162,26 +312,13 @@
html.gsub!(/([\s\W])_([^_]+)_([\s\W])/m, '\1<em>\2</em>\3')
html.gsub!(/([\s\W])\+([^\+]+)\+([\s\W])/m, '\1<tt>\2</tt>\3')
html.gsub!(/([\s\W])\^([^\^]+)\^([\s\W])/m, '\1<sup>\2</sup>\3')
html.gsub!(/([\s\W])\~([^\~]+)\~([\s\W])/m, '\1<sub>\2</sub>\3')
- # Don't have lookbehind so have to capture and re-insert
- html.gsub!(/(^|[^\\])\{(\w[\w\-]+\w)\}/) do
- if self.document.defines.has_key?($2)
- # Substitute from user defines first
- $1 + self.document.defines[$2]
- elsif Asciidoctor::INTRINSICS.has_key?($2)
- # Then do intrinsics
- $1 + Asciidoctor::INTRINSICS[$2]
- else
- # leave everything else alone
- "#{$1}{#{$2}}"
- end
- end
-
html.gsub!(/\\([\{\}\-])/, '\1')
html.gsub!(/linkgit:([^\]]+)\[(\d+)\]/, '<a href="\1.html">\1(\2)</a>')
html.gsub!(/link:([^\[]+)(\[+[^\]]*\]+)/ ) { "<a href=\"#{$1}\">#{$2.gsub( /(^\[|\]$)/,'' )}</a>" }
+ html.gsub!(Asciidoctor::REGEXP[:line_break], '\1<br/>')
html
end
end
# end private
end