lib/asciidoctor/abstract_block.rb in asciidoctor-1.5.6.2 vs lib/asciidoctor/abstract_block.rb in asciidoctor-1.5.7

- old
+ new

@@ -1,34 +1,34 @@ # encoding: UTF-8 module Asciidoctor class AbstractBlock < AbstractNode - # Public: The types of content that this block can accomodate - attr_accessor :content_model - - # Public: Substitutions to be applied to content in this block - attr_reader :subs - # Public: Get the Array of Asciidoctor::AbstractBlock sub-blocks for this block attr_reader :blocks - # Public: Set the Integer level of this Section or the Section level in which this Block resides - attr_accessor :level - - # Public: Get/Set the String style (block type qualifier) for this block. - attr_accessor :style - # Public: Set the caption for this block attr_writer :caption + # Public: The types of content that this block can accomodate + attr_accessor :content_model + + # Public: Set the Integer level of this Section or the Section level in which this Block resides + attr_accessor :level + # Public: Get/Set the number of this block (if section, relative to parent, otherwise absolute) # Only assigned to section if automatic section numbering is enabled # Only assigned to formal block (block with title) if corresponding caption attribute is present attr_accessor :number # Public: Gets/Sets the location in the AsciiDoc source where this block begins attr_accessor :source_location + # Public: Get/Set the String style (block type qualifier) for this block. + attr_accessor :style + + # Public: Substitutions to be applied to content in this block + attr_reader :subs + def initialize parent, context, opts = {} super @content_model = :compound @blocks = [] @subs = [] @@ -50,19 +50,20 @@ def inline? false end - # Public: Update the context of this block. - # - # This method changes the context of this block. It also - # updates the node name accordingly. - def context=(context) - @context = context - @node_name = context.to_s + # Public: Get the source file where this block started + def file + @source_location && @source_location.file end + # Public: Get the source line number where this block started + def lineno + @source_location && @source_location.lineno + end + # Public: Get the converted String content for this Block. If the block # has child blocks, the content method should cause them to be # converted and returned as content that can be included in the # parent block's template. def convert @@ -77,153 +78,21 @@ # children appropriate to content model that this block supports. def content @blocks.map {|b| b.convert } * LF end - # Public: Get the source file where this block started - def file - @source_location ? @source_location.file : nil - end - - # Public: Get the source line number where this block started - def lineno - @source_location ? @source_location.lineno : nil - end - - # Public: A convenience method that checks whether the specified - # substitution is enabled for this block. + # Public: Update the context of this block. # - # name - The Symbol substitution name + # This method changes the context of this block. It also updates the node name accordingly. # - # Returns A Boolean indicating whether the specified substitution is - # enabled for this block - def sub? name - @subs.include? name - end - - # Public: A convenience method that checks whether the title of this block is defined. + # context - the context Symbol context to assign to this block # - # Returns a [Boolean] indicating whether this block has a title. - def title? - @title ? true : false + # Returns the new context Symbol assigned to this block + def context= context + @node_name = (@context = context).to_s end - # Public: Get the String title of this Block with title substitions applied - # - # The following substitutions are applied to block and section titles: - # - # :specialcharacters, :quotes, :replacements, :macros, :attributes and :post_replacements - # - # Examples - # - # block.title = "Foo 3^ # {two-colons} Bar(1)" - # block.title - # => "Foo 3^ # :: Bar(1)" - # - # Returns the converted String title for this Block, or nil if the source title is falsy - def title - # prevent substitutions from being applied to title multiple times - @title_converted ? @converted_title : (@converted_title = (@title_converted = true) && @title && (apply_title_subs @title)) - end - - # Public: Set the String block title. - # - # Returns the new String title assigned to this Block - def title= val - @title, @title_converted = val, nil - end - - # Gets the caption for this block. - # - # This method routes the deprecated use of the caption method on an - # admonition block to the textlabel attribute. - # - # Returns the [String] caption for this block (or the value of the textlabel - # attribute if this is an admonition block). - def caption - @context == :admonition ? @attributes['textlabel'] : @caption - end - - # Public: Convenience method that returns the interpreted title of the Block - # with the caption prepended. - # - # Concatenates the value of this Block's caption instance variable and the - # return value of this Block's title method. No space is added between the - # two values. If the Block does not have a caption, the interpreted title is - # returned. - # - # Returns the converted String title prefixed with the caption, or just the - # converted String title if no caption is set - def captioned_title - %(#{@caption}#{title}) - end - - # Public: Returns the converted alt text for this block image. - # - # Returns the [String] value of the alt attribute with XML special character - # and replacement substitutions applied. - def alt - if (text = @attributes['alt']) - if text == @attributes['default-alt'] - sub_specialchars text - else - text = sub_specialchars text - (ReplaceableTextRx.match? text) ? (sub_replacements text) : text - end - end - end - - # Public: Generate cross reference text (xreftext) that can be used to refer - # to this block. - # - # Use the explicit reftext for this block, if specified, retrieved from the - # {#reftext} method. Otherwise, if this is a section or captioned block (a - # block with both a title and caption), generate the xreftext according to - # the value of the xrefstyle argument (e.g., full, short). This logic may - # leverage the {Substitutors#sub_quotes} method to apply formatting to the - # text. If this is not a captioned block, return the title, if present, or - # nil otherwise. - # - # xrefstyle - An optional String that specifies the style to use to format - # the xreftext ('full', 'short', or 'basic') (default: nil). - # - # Returns the generated [String] xreftext used to refer to this block or - # nothing if there isn't sufficient information to generate one. - def xreftext xrefstyle = nil - if (val = reftext) && !val.empty? - val - # NOTE xrefstyle only applies to blocks with a title and a caption or number - elsif xrefstyle && @title && @caption - case xrefstyle - when 'full' - quoted_title = sprintf sub_quotes(@document.compat_mode ? %q(``%s'') : '"`%s`"'), title - if @number && (prefix = @document.attributes[@context == :image ? 'figure-caption' : %(#{@context}-caption)]) - %(#{prefix} #{@number}, #{quoted_title}) - else - %(#{@caption.chomp '. '}, #{quoted_title}) - end - when 'short' - if @number && (prefix = @document.attributes[@context == :image ? 'figure-caption' : %(#{@context}-caption)]) - %(#{prefix} #{@number}) - else - @caption.chomp '. ' - end - else # 'basic' - title - end - else - title - end - end - - # Public: Determine whether this Block contains block content - # - # Returns A Boolean indicating whether this Block has block content - def blocks? - !@blocks.empty? - end - # Public: Append a content block to this block's list of blocks. # # block - The new child block. # # Examples @@ -237,43 +106,23 @@ # block.blocks.size # # => 2 # # Returns The parent Block def << block - # parent assignment pending refactor - #block.parent = self + block.parent = self unless block.parent == self @blocks << block self end # NOTE append alias required for adapting to a Java API alias append << - # Public: Get the Array of child Section objects + # Public: Determine whether this Block contains block content # - # Only applies to Document and Section instances - # - # Examples - # - # doc << (sect1 = Section.new doc, 1, false) - # sect1.title = 'Section 1' - # para1 = Block.new sect1, :paragraph, :source => 'Paragraph 1' - # para2 = Block.new sect1, :paragraph, :source => 'Paragraph 2' - # sect1 << para1 << para2 - # sect1 << (sect1_1 = Section.new sect1, 2, false) - # sect1_1.title = 'Section 1.1' - # sect1_1 << (Block.new sect1_1, :paragraph, :source => 'Paragraph 3') - # sect1.blocks? - # # => true - # sect1.blocks.size - # # => 3 - # sect1.sections.size - # # => 1 - # - # Returns an [Array] of Section objects - def sections - @blocks.select {|block| block.context == :section } + # Returns A Boolean indicating whether this Block has block content + def blocks? + !@blocks.empty? end # Public: Check whether this block has any child Section objects. # # Only applies to Document and Section instances @@ -281,41 +130,10 @@ # Returns A [Boolean] to indicate whether this block has child Section objects def sections? @next_section_index > 0 end -# stage the Enumerable mixin until we're sure we've got it right -=begin - include ::Enumerable - - # Public: Yield the block on this block node and all its descendant - # block node children to satisfy the Enumerable contract. - # - # Returns nothing - def each &block - # yucky, dlist is a special case - if @context == :dlist - @blocks.flatten.each &block - else - #yield self.header if @context == :document && header? - @blocks.each &block - end - end - - #-- - # TODO is there a way to make this lazy? - def each_recursive &block - block = lambda {|node| node } unless block_given? - results = [] - self.each do |node| - results << block.call(node) - results.concat(node.each_recursive(&block)) if ::Enumerable === node - end - block_given? ? results : results.to_enum - end -=end - # Public: Query for all descendant block-level nodes in the document tree # that match the specified selector (context, style, id, and/or role). If a # Ruby block is given, it's used as an additional filter. If no selector or # Ruby block is supplied, all block-level nodes in the tree are returned. # @@ -375,28 +193,192 @@ end end end result end + alias query find_by # Move to the next adjacent block in document order. If the current block is the last # item in a list, this method will return the following sibling of the list block. def next_adjacent_block (sib = (p = parent).blocks[(p.blocks.find_index self) + 1]) ? sib : p.next_adjacent_block unless @context == :document end + # Public: Get the Array of child Section objects + # + # Only applies to Document and Section instances + # + # Examples + # + # doc << (sect1 = Section.new doc, 1) + # sect1.title = 'Section 1' + # para1 = Block.new sect1, :paragraph, :source => 'Paragraph 1' + # para2 = Block.new sect1, :paragraph, :source => 'Paragraph 2' + # sect1 << para1 << para2 + # sect1 << (sect1_1 = Section.new sect1, 2) + # sect1_1.title = 'Section 1.1' + # sect1_1 << (Block.new sect1_1, :paragraph, :source => 'Paragraph 3') + # sect1.blocks? + # # => true + # sect1.blocks.size + # # => 3 + # sect1.sections.size + # # => 1 + # + # Returns an [Array] of Section objects + def sections + @blocks.select {|block| block.context == :section } + end + + # Public: Returns the converted alt text for this block image. + # + # Returns the [String] value of the alt attribute with XML special character + # and replacement substitutions applied. + def alt + if (text = @attributes['alt']) + if text == @attributes['default-alt'] + sub_specialchars text + else + text = sub_specialchars text + (ReplaceableTextRx.match? text) ? (sub_replacements text) : text + end + end + end + + # Gets the caption for this block. + # + # This method routes the deprecated use of the caption method on an + # admonition block to the textlabel attribute. + # + # Returns the [String] caption for this block (or the value of the textlabel + # attribute if this is an admonition block). + def caption + @context == :admonition ? @attributes['textlabel'] : @caption + end + + # Public: Convenience method that returns the interpreted title of the Block + # with the caption prepended. + # + # Concatenates the value of this Block's caption instance variable and the + # return value of this Block's title method. No space is added between the + # two values. If the Block does not have a caption, the interpreted title is + # returned. + # + # Returns the converted String title prefixed with the caption, or just the + # converted String title if no caption is set + def captioned_title + %(#{@caption}#{title}) + end + + # Public: Retrieve the list marker keyword for the specified list type. + # + # For use in the HTML type attribute. + # + # list_type - the type of list; default to the @style if not specified + # + # Returns the single-character [String] keyword that represents the marker for the specified list type + def list_marker_keyword list_type = nil + ORDERED_LIST_KEYWORDS[list_type || @style] + end + + # Public: Get the String title of this Block with title substitions applied + # + # The following substitutions are applied to block and section titles: + # + # :specialcharacters, :quotes, :replacements, :macros, :attributes and :post_replacements + # + # Examples + # + # block.title = "Foo 3^ # {two-colons} Bar(1)" + # block.title + # => "Foo 3^ # :: Bar(1)" + # + # Returns the converted String title for this Block, or nil if the source title is falsy + def title + # prevent substitutions from being applied to title multiple times + @title_converted ? @converted_title : (@converted_title = (@title_converted = true) && @title && (apply_title_subs @title)) + end + + # Public: A convenience method that checks whether the title of this block is defined. + # + # Returns a [Boolean] indicating whether this block has a title. + def title? + @title ? true : false + end + + # Public: Set the String block title. + # + # Returns the new String title assigned to this Block + def title= val + @title, @title_converted = val, nil + end + + # Public: A convenience method that checks whether the specified + # substitution is enabled for this block. + # + # name - The Symbol substitution name + # + # Returns A Boolean indicating whether the specified substitution is + # enabled for this block + def sub? name + @subs.include? name + end + # Public: Remove a substitution from this block # # sub - The Symbol substitution name # # Returns nothing def remove_sub sub @subs.delete sub nil end + # Public: Generate cross reference text (xreftext) that can be used to refer + # to this block. + # + # Use the explicit reftext for this block, if specified, retrieved from the + # {#reftext} method. Otherwise, if this is a section or captioned block (a + # block with both a title and caption), generate the xreftext according to + # the value of the xrefstyle argument (e.g., full, short). This logic may + # leverage the {Substitutors#sub_quotes} method to apply formatting to the + # text. If this is not a captioned block, return the title, if present, or + # nil otherwise. + # + # xrefstyle - An optional String that specifies the style to use to format + # the xreftext ('full', 'short', or 'basic') (default: nil). + # + # Returns the generated [String] xreftext used to refer to this block or + # nothing if there isn't sufficient information to generate one. + def xreftext xrefstyle = nil + if (val = reftext) && !val.empty? + val + # NOTE xrefstyle only applies to blocks with a title and a caption or number + elsif xrefstyle && @title && @caption + case xrefstyle + when 'full' + quoted_title = sprintf sub_quotes(@document.compat_mode ? %q(``%s'') : '"`%s`"'), title + if @number && (prefix = @document.attributes[@context == :image ? 'figure-caption' : %(#{@context}-caption)]) + %(#{prefix} #{@number}, #{quoted_title}) + else + %(#{@caption.chomp '. '}, #{quoted_title}) + end + when 'short' + if @number && (prefix = @document.attributes[@context == :image ? 'figure-caption' : %(#{@context}-caption)]) + %(#{prefix} #{@number}) + else + @caption.chomp '. ' + end + else # 'basic' + title + end + else + title + end + end + # Public: Generate and assign caption to block if not already assigned. # # If the block has a title and a caption prefix is available for this block, # then build a caption from this information, assign it a number and store it # to the caption attribute on the block. @@ -419,45 +401,40 @@ nil end end end - # Internal: Assign the next index (0-based) and number (1-based) to the section + # Internal: Assign the next index (0-based) and numeral (1-based) to the section. + # If the section is an appendix, the numeral is a letter (starting with A). This + # method also assigns the appendix caption. # + # section - The section to which to assign the next index and numeral. + # # Assign to the specified section the next index and, if the section is - # numbered, number within this block (its parent). + # numbered, the numeral within this block (its parent). # # Returns nothing - def enumerate_section section + def assign_numeral section @next_section_index = (section.index = @next_section_index) + 1 - if (sectname = section.sectname) == 'appendix' - section.number = @document.counter 'appendix-number', 'A' - if (caption = @document.attributes['appendix-caption']) - section.caption = %(#{caption} #{section.number}: ) + if (like = section.numbered) + if (sectname = section.sectname) == 'appendix' + section.number = @document.counter 'appendix-number', 'A' + if (caption = @document.attributes['appendix-caption']) + section.caption = %(#{caption} #{section.number}: ) + else + section.caption = %(#{section.number}. ) + end + # NOTE currently chapters in a book doctype are sequential even for multi-part books (see #979) + elsif sectname == 'chapter' || like == :chapter + section.number = @document.counter 'chapter-number', 1 else - section.caption = %(#{section.number}. ) + @next_section_number = (section.number = @next_section_number) + 1 end - # NOTE currently chapters in a book doctype are sequential even for multi-part books (see #979) - elsif sectname == 'chapter' - section.number = @document.counter 'chapter-number', 1 - else - @next_section_number = (section.number = @next_section_number) + 1 - end if section.numbered + end nil end - # Public: Retrieve the list marker keyword for the specified list type. - # - # For use in the HTML type attribute. - # - # list_type - the type of list; default to the @style if not specified - # - # Returns the single-character [String] keyword that represents the marker for the specified list type - def list_marker_keyword list_type = nil - ORDERED_LIST_KEYWORDS[list_type || @style] - end - # Internal: Reassign the section indexes # # Walk the descendents of the current Document or Section # and reassign the section 0-based index value to each Section # as it appears in document order. @@ -469,12 +446,43 @@ def reindex_sections @next_section_index = 0 @next_section_number = 1 @blocks.each do |block| if block.context == :section - enumerate_section block + assign_numeral block block.reindex_sections end end end + +# stage the Enumerable mixin until we're sure we've got it right +=begin + include ::Enumerable + + # Public: Yield the block on this block node and all its descendant + # block node children to satisfy the Enumerable contract. + # + # Returns nothing + def each &block + # yucky, dlist is a special case + if @context == :dlist + @blocks.flatten.each &block + else + #yield self.header if @context == :document && header? + @blocks.each &block + end + end + + #-- + # TODO is there a way to make this lazy? + def each_recursive &block + block = lambda {|node| node } unless block_given? + results = [] + self.each do |node| + results << block.call(node) + results.concat(node.each_recursive(&block)) if ::Enumerable === node + end + block_given? ? results : results.to_enum + end +=end end end