asciidoctor 0.1.4 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of asciidoctor might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +209 -25
- data/{LICENSE → LICENSE.adoc} +4 -3
- data/README.adoc +392 -395
- data/Rakefile +94 -137
- data/benchmark/benchmark.rb +127 -0
- data/benchmark/sample-data/mdbasics.adoc +334 -0
- data/bin/asciidoctor +5 -8
- data/bin/asciidoctor-safe +4 -8
- data/compat/asciidoc.conf +78 -11
- data/compat/font-awesome-3-compat.css +397 -0
- data/data/stylesheets/asciidoctor-default.css +399 -0
- data/data/stylesheets/coderay-asciidoctor.css +89 -0
- data/features/open_block.feature +92 -0
- data/features/pass_block.feature +66 -0
- data/features/step_definitions.rb +42 -0
- data/features/text_formatting.feature +55 -0
- data/features/xref.feature +116 -0
- data/lib/asciidoctor.rb +1155 -605
- data/lib/asciidoctor/abstract_block.rb +157 -71
- data/lib/asciidoctor/abstract_node.rb +150 -93
- data/lib/asciidoctor/attribute_list.rb +85 -90
- data/lib/asciidoctor/block.rb +51 -24
- data/lib/asciidoctor/callouts.rb +4 -7
- data/lib/asciidoctor/cli.rb +3 -0
- data/lib/asciidoctor/cli/invoker.rb +86 -76
- data/lib/asciidoctor/cli/options.rb +111 -61
- data/lib/asciidoctor/converter.rb +232 -0
- data/lib/asciidoctor/converter/base.rb +58 -0
- data/lib/asciidoctor/converter/composite.rb +66 -0
- data/lib/asciidoctor/converter/docbook45.rb +94 -0
- data/lib/asciidoctor/converter/docbook5.rb +684 -0
- data/lib/asciidoctor/converter/factory.rb +225 -0
- data/lib/asciidoctor/converter/html5.rb +1081 -0
- data/lib/asciidoctor/converter/template.rb +296 -0
- data/lib/asciidoctor/core_ext.rb +7 -0
- data/lib/asciidoctor/core_ext/object/nil_or_empty.rb +23 -0
- data/lib/asciidoctor/core_ext/string/chr.rb +6 -0
- data/lib/asciidoctor/core_ext/symbol/length.rb +6 -0
- data/lib/asciidoctor/document.rb +590 -304
- data/lib/asciidoctor/extensions.rb +1100 -308
- data/lib/asciidoctor/helpers.rb +109 -46
- data/lib/asciidoctor/inline.rb +16 -9
- data/lib/asciidoctor/list.rb +23 -15
- data/lib/asciidoctor/opal_ext.rb +4 -0
- data/lib/asciidoctor/opal_ext/comparable.rb +38 -0
- data/lib/asciidoctor/opal_ext/dir.rb +13 -0
- data/lib/asciidoctor/opal_ext/error.rb +2 -0
- data/lib/asciidoctor/opal_ext/file.rb +125 -0
- data/lib/asciidoctor/{lexer.rb → parser.rb} +646 -455
- data/lib/asciidoctor/path_resolver.rb +141 -77
- data/lib/asciidoctor/reader.rb +257 -187
- data/lib/asciidoctor/section.rb +12 -16
- data/lib/asciidoctor/stylesheets.rb +91 -0
- data/lib/asciidoctor/substitutors.rb +1548 -0
- data/lib/asciidoctor/table.rb +73 -57
- data/lib/asciidoctor/timings.rb +39 -0
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +22 -14
- data/man/asciidoctor.adoc +18 -10
- data/test/attributes_test.rb +314 -14
- data/test/blocks_test.rb +763 -118
- data/test/converter_test.rb +352 -0
- data/test/document_test.rb +518 -199
- data/test/extensions_test.rb +273 -103
- data/test/fixtures/asciidoc_index.txt +27 -13
- data/test/fixtures/basic-docinfo.xml +1 -1
- data/test/fixtures/chapter-a.adoc +3 -0
- data/test/fixtures/custom-backends/erb/html5/block_paragraph.html.erb +6 -0
- data/test/fixtures/docinfo.xml +1 -1
- data/test/fixtures/include-file.asciidoc +2 -0
- data/test/fixtures/master.adoc +5 -0
- data/test/invoker_test.rb +173 -61
- data/test/links_test.rb +97 -21
- data/test/lists_test.rb +181 -22
- data/test/options_test.rb +86 -2
- data/test/paragraphs_test.rb +47 -5
- data/test/{lexer_test.rb → parser_test.rb} +128 -57
- data/test/paths_test.rb +36 -1
- data/test/preamble_test.rb +25 -17
- data/test/reader_test.rb +404 -249
- data/test/sections_test.rb +623 -58
- data/test/substitutions_test.rb +609 -132
- data/test/tables_test.rb +198 -24
- data/test/test_helper.rb +101 -31
- data/test/text_test.rb +88 -31
- metadata +160 -64
- data/Gemfile +0 -12
- data/Guardfile +0 -18
- data/asciidoctor.gemspec +0 -143
- data/lib/asciidoctor/backends/_stylesheets.rb +0 -466
- data/lib/asciidoctor/backends/base_template.rb +0 -114
- data/lib/asciidoctor/backends/docbook45.rb +0 -774
- data/lib/asciidoctor/backends/docbook5.rb +0 -103
- data/lib/asciidoctor/backends/html5.rb +0 -1214
- data/lib/asciidoctor/renderer.rb +0 -259
- data/lib/asciidoctor/substituters.rb +0 -1083
- data/test/fixtures/asciidoc.txt +0 -105
- data/test/fixtures/ascshort.txt +0 -32
- data/test/fixtures/list_elements.asciidoc +0 -10
- data/test/renderer_test.rb +0 -162
| @@ -10,7 +10,7 @@ module Asciidoctor | |
| 10 10 | 
             
            #    attrlist = Asciidoctor::AttributeList.new('astyle')
         | 
| 11 11 | 
             
            #
         | 
| 12 12 | 
             
            #    attrlist.parse
         | 
| 13 | 
            -
            #    => {0 => 'astyle'} | 
| 13 | 
            +
            #    => {0 => 'astyle'}
         | 
| 14 14 | 
             
            #
         | 
| 15 15 | 
             
            #    attrlist.rekey(['style'])
         | 
| 16 16 | 
             
            #    => {'style' => 'astyle'}
         | 
| @@ -18,55 +18,58 @@ module Asciidoctor | |
| 18 18 | 
             
            #    attrlist = Asciidoctor::AttributeList.new('quote, Famous Person, Famous Book (2001)')
         | 
| 19 19 | 
             
            #
         | 
| 20 20 | 
             
            #    attrlist.parse(['style', 'attribution', 'citetitle'])
         | 
| 21 | 
            -
            #    => {'style' => 'quote', 'attribution' => 'Famous Person', 'citetitle' => 'Famous Book (2001)'} | 
| 21 | 
            +
            #    => {'style' => 'quote', 'attribution' => 'Famous Person', 'citetitle' => 'Famous Book (2001)'}
         | 
| 22 22 | 
             
            #
         | 
| 23 23 | 
             
            class AttributeList
         | 
| 24 24 |  | 
| 25 25 | 
             
              # Public: Regular expressions for detecting the boundary of a value
         | 
| 26 | 
            -
               | 
| 26 | 
            +
              BoundaryRxs = {
         | 
| 27 27 | 
             
                '"' => /.*?[^\\](?=")/,
         | 
| 28 28 | 
             
                '\'' => /.*?[^\\](?=')/,
         | 
| 29 29 | 
             
                ',' => /.*?(?=[ \t]*(,|$))/
         | 
| 30 30 | 
             
              }
         | 
| 31 31 |  | 
| 32 32 | 
             
              # Public: Regular expressions for unescaping quoted characters
         | 
| 33 | 
            -
               | 
| 34 | 
            -
                ' | 
| 35 | 
            -
                ' | 
| 33 | 
            +
              EscapedQuoteRxs = {
         | 
| 34 | 
            +
                '"' => /\\"/,
         | 
| 35 | 
            +
                '\'' => /\\'/
         | 
| 36 36 | 
             
              }
         | 
| 37 37 |  | 
| 38 | 
            +
              # Public: A regular expression for an attribute name
         | 
| 39 | 
            +
              # TODO named attributes cannot contain dash characters
         | 
| 40 | 
            +
              NameRx = /[A-Za-z:_][A-Za-z:_\-.]*/
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              BlankRx = /[ \t]+/
         | 
| 43 | 
            +
             | 
| 38 44 | 
             
              # Public: Regular expressions for skipping blanks and delimiters
         | 
| 39 | 
            -
               | 
| 40 | 
            -
                :blank =>  | 
| 45 | 
            +
              SkipRxs = {
         | 
| 46 | 
            +
                :blank => BlankRx,
         | 
| 41 47 | 
             
                ',' => /[ \t]*(,|$)/
         | 
| 42 48 | 
             
              }
         | 
| 43 49 |  | 
| 44 | 
            -
               | 
| 45 | 
            -
              # TODO named attributes cannot contain dash characters
         | 
| 46 | 
            -
              NAME_PATTERN = /[A-Za-z:_][A-Za-z:_\-\.]*/
         | 
| 47 | 
            -
             | 
| 48 | 
            -
              def initialize(source, block = nil, quotes = ['\'', '"'], delimiter = ',', escape_char = '\\')
         | 
| 50 | 
            +
              def initialize source, block = nil, delimiter = ','
         | 
| 49 51 | 
             
                @scanner = ::StringScanner.new source
         | 
| 50 52 | 
             
                @block = block
         | 
| 51 | 
            -
                @quotes = quotes
         | 
| 52 | 
            -
                @escape_char = escape_char
         | 
| 53 53 | 
             
                @delimiter = delimiter
         | 
| 54 | 
            +
                @delimiter_skip_pattern = SkipRxs[delimiter]
         | 
| 55 | 
            +
                @delimiter_boundary_pattern = BoundaryRxs[delimiter]
         | 
| 54 56 | 
             
                @attributes = nil
         | 
| 55 57 | 
             
              end
         | 
| 56 58 |  | 
| 57 | 
            -
              def parse_into | 
| 58 | 
            -
                attributes.update(parse | 
| 59 | 
            +
              def parse_into attributes, posattrs = []
         | 
| 60 | 
            +
                attributes.update(parse posattrs)
         | 
| 59 61 | 
             
              end
         | 
| 60 62 |  | 
| 61 | 
            -
              def parse | 
| 62 | 
            -
                return  | 
| 63 | 
            +
              def parse posattrs = []
         | 
| 64 | 
            +
                # return if already parsed
         | 
| 65 | 
            +
                return @attributes if @attributes
         | 
| 63 66 |  | 
| 64 67 | 
             
                @attributes = {}
         | 
| 65 | 
            -
                #  | 
| 68 | 
            +
                # QUESTION do we want to store the attribute list as the zero-index attribute?
         | 
| 66 69 | 
             
                #attributes[0] = @scanner.string
         | 
| 67 70 | 
             
                index = 0
         | 
| 68 71 |  | 
| 69 | 
            -
                while parse_attribute | 
| 72 | 
            +
                while parse_attribute index, posattrs
         | 
| 70 73 | 
             
                  break if @scanner.eos?
         | 
| 71 74 | 
             
                  skip_delimiter
         | 
| 72 75 | 
             
                  index += 1
         | 
| @@ -75,144 +78,136 @@ class AttributeList | |
| 75 78 | 
             
                @attributes
         | 
| 76 79 | 
             
              end
         | 
| 77 80 |  | 
| 78 | 
            -
              def rekey | 
| 79 | 
            -
                AttributeList.rekey | 
| 81 | 
            +
              def rekey posattrs
         | 
| 82 | 
            +
                AttributeList.rekey @attributes, posattrs
         | 
| 80 83 | 
             
              end
         | 
| 81 84 |  | 
| 82 | 
            -
              def self.rekey | 
| 85 | 
            +
              def self.rekey attributes, pos_attrs
         | 
| 83 86 | 
             
                pos_attrs.each_with_index do |key, index|
         | 
| 84 | 
            -
                  next  | 
| 87 | 
            +
                  next unless key
         | 
| 85 88 | 
             
                  pos = index + 1
         | 
| 86 | 
            -
                   | 
| 89 | 
            +
                  if (val = attributes[pos])
         | 
| 90 | 
            +
                    # QUESTION should we delete the positional key?
         | 
| 87 91 | 
             
                    attributes[key] = val
         | 
| 88 | 
            -
                    #QUESTION should we delete the positional key?
         | 
| 89 | 
            -
                    #attributes.delete pos
         | 
| 90 92 | 
             
                  end
         | 
| 91 93 | 
             
                end
         | 
| 92 94 |  | 
| 93 95 | 
             
                attributes
         | 
| 94 96 | 
             
              end
         | 
| 95 97 |  | 
| 96 | 
            -
              def parse_attribute | 
| 98 | 
            +
              def parse_attribute index = 0, pos_attrs = []
         | 
| 97 99 | 
             
                single_quoted_value = false
         | 
| 98 100 | 
             
                skip_blank
         | 
| 99 | 
            -
                 | 
| 100 | 
            -
                 | 
| 101 | 
            -
             | 
| 101 | 
            +
                # example: "quote"
         | 
| 102 | 
            +
                if (first = @scanner.peek(1)) == '"'
         | 
| 103 | 
            +
                  name = parse_attribute_value @scanner.get_byte
         | 
| 102 104 | 
             
                  value = nil
         | 
| 105 | 
            +
                # example: 'quote'
         | 
| 106 | 
            +
                elsif first == '\''
         | 
| 103 107 | 
             
                  name = parse_attribute_value @scanner.get_byte
         | 
| 104 | 
            -
                   | 
| 105 | 
            -
             | 
| 106 | 
            -
                  end
         | 
| 108 | 
            +
                  value = nil
         | 
| 109 | 
            +
                  single_quoted_value = true
         | 
| 107 110 | 
             
                else
         | 
| 108 111 | 
             
                  name = scan_name
         | 
| 109 112 |  | 
| 110 113 | 
             
                  skipped = 0
         | 
| 111 114 | 
             
                  c = nil
         | 
| 112 115 | 
             
                  if @scanner.eos?
         | 
| 113 | 
            -
                     | 
| 114 | 
            -
                      return false
         | 
| 115 | 
            -
                    end
         | 
| 116 | 
            +
                    return false unless name
         | 
| 116 117 | 
             
                  else
         | 
| 117 118 | 
             
                    skipped = skip_blank || 0
         | 
| 118 119 | 
             
                    c = @scanner.get_byte
         | 
| 119 120 | 
             
                  end
         | 
| 120 121 |  | 
| 121 122 | 
             
                  # example: quote
         | 
| 122 | 
            -
                  if c | 
| 123 | 
            +
                  if !c || c == @delimiter
         | 
| 123 124 | 
             
                    value = nil
         | 
| 124 125 | 
             
                  # example: Sherlock Holmes || =foo=
         | 
| 125 | 
            -
                  elsif c != '=' || name | 
| 126 | 
            -
                     | 
| 127 | 
            -
                    name = '' if name.nil?
         | 
| 128 | 
            -
                    name += ' ' * skipped + c
         | 
| 129 | 
            -
                    name += remainder unless remainder.nil?
         | 
| 126 | 
            +
                  elsif c != '=' || !name
         | 
| 127 | 
            +
                    name = %(#{name}#{' ' * skipped}#{c}#{scan_to_delimiter})
         | 
| 130 128 | 
             
                    value = nil
         | 
| 131 129 | 
             
                  else
         | 
| 132 130 | 
             
                    skip_blank
         | 
| 133 | 
            -
                     | 
| 134 | 
            -
             | 
| 135 | 
            -
                       | 
| 136 | 
            -
                    else
         | 
| 137 | 
            -
                      c = @scanner.get_byte
         | 
| 138 | 
            -
             | 
| 139 | 
            -
                      # example: foo="bar" || foo='bar' || foo="ba\"zaar" || foo='ba\'zaar' || foo='ba"zaar' (all spaces ignored)
         | 
| 140 | 
            -
                      if @quotes.include? c
         | 
| 131 | 
            +
                    if @scanner.peek(1)
         | 
| 132 | 
            +
                      # example: foo="bar" || foo="ba\"zaar"
         | 
| 133 | 
            +
                      if (c = @scanner.get_byte) == '"'
         | 
| 141 134 | 
             
                        value = parse_attribute_value c
         | 
| 142 | 
            -
             | 
| 143 | 
            -
             | 
| 144 | 
            -
                         | 
| 135 | 
            +
                      # example: foo='bar' || foo='ba\'zaar' || foo='ba"zaar'
         | 
| 136 | 
            +
                      elsif c == '\''
         | 
| 137 | 
            +
                        value = parse_attribute_value c
         | 
| 138 | 
            +
                        single_quoted_value = true
         | 
| 139 | 
            +
                      # example: foo=,
         | 
| 140 | 
            +
                      elsif c == @delimiter
         | 
| 141 | 
            +
                        value = nil
         | 
| 145 142 | 
             
                      # example: foo=bar (all spaces ignored)
         | 
| 146 | 
            -
                       | 
| 147 | 
            -
                        value = c | 
| 143 | 
            +
                      else
         | 
| 144 | 
            +
                        value = %(#{c}#{scan_to_delimiter})
         | 
| 145 | 
            +
                        return true if value == 'None'
         | 
| 148 146 | 
             
                      end
         | 
| 149 147 | 
             
                    end
         | 
| 150 148 | 
             
                  end
         | 
| 151 149 | 
             
                end
         | 
| 152 150 |  | 
| 153 | 
            -
                if value | 
| 154 | 
            -
                  resolved_name = single_quoted_value && !@block.nil? ? @block.apply_normal_subs(name) : name
         | 
| 155 | 
            -
                  if !(pos_name = pos_attrs[index]).nil?
         | 
| 156 | 
            -
                    @attributes[pos_name] = resolved_name
         | 
| 157 | 
            -
                  else
         | 
| 158 | 
            -
                    #@attributes[index + 1] = resolved_name
         | 
| 159 | 
            -
                  end
         | 
| 160 | 
            -
                  # not sure if we want to always assign the positional key
         | 
| 161 | 
            -
                  @attributes[index + 1] = resolved_name
         | 
| 162 | 
            -
                  # not sure if I want this assignment or not
         | 
| 163 | 
            -
                  #@attributes[resolved_name] = nil
         | 
| 164 | 
            -
                else
         | 
| 165 | 
            -
                  resolved_value = value
         | 
| 151 | 
            +
                if value
         | 
| 166 152 | 
             
                  # example: options="opt1,opt2,opt3"
         | 
| 167 153 | 
             
                  # opts is an alias for options
         | 
| 168 | 
            -
                   | 
| 154 | 
            +
                  case name
         | 
| 155 | 
            +
                  when 'options', 'opts'
         | 
| 169 156 | 
             
                    name = 'options'
         | 
| 170 | 
            -
                     | 
| 171 | 
            -
             | 
| 172 | 
            -
             | 
| 173 | 
            -
             | 
| 174 | 
            -
             | 
| 157 | 
            +
                    value.split(',').each {|o| @attributes[%(#{o.strip}-option)] = '' }
         | 
| 158 | 
            +
                    @attributes[name] = value
         | 
| 159 | 
            +
                  when 'title'
         | 
| 160 | 
            +
                    @attributes[name] = value
         | 
| 161 | 
            +
                  else
         | 
| 162 | 
            +
                    @attributes[name] = single_quoted_value && !value.empty? && @block ? (@block.apply_normal_subs value) : value
         | 
| 163 | 
            +
                  end
         | 
| 164 | 
            +
                else
         | 
| 165 | 
            +
                  resolved_name = single_quoted_value && !name.empty? && @block ? (@block.apply_normal_subs name) : name
         | 
| 166 | 
            +
                  if (pos_name = pos_attrs[index])
         | 
| 167 | 
            +
                    @attributes[pos_name] = resolved_name
         | 
| 175 168 | 
             
                  end
         | 
| 176 | 
            -
                   | 
| 169 | 
            +
                  # QUESTION should we always assign the positional key?
         | 
| 170 | 
            +
                  @attributes[index + 1] = resolved_name
         | 
| 171 | 
            +
                  # QUESTION should we assign the resolved name as an attribute?
         | 
| 172 | 
            +
                  #@attributes[resolved_name] = nil
         | 
| 177 173 | 
             
                end
         | 
| 178 174 |  | 
| 179 175 | 
             
                true
         | 
| 180 176 | 
             
              end
         | 
| 181 177 |  | 
| 182 | 
            -
              def parse_attribute_value | 
| 178 | 
            +
              def parse_attribute_value quote
         | 
| 183 179 | 
             
                # empty quoted value
         | 
| 184 180 | 
             
                if @scanner.peek(1) == quote
         | 
| 185 | 
            -
                  @scanner.get_byte | 
| 181 | 
            +
                  @scanner.get_byte
         | 
| 186 182 | 
             
                  return ''
         | 
| 187 183 | 
             
                end
         | 
| 188 184 |  | 
| 189 | 
            -
                value = scan_to_quote quote
         | 
| 190 | 
            -
                if value.nil?
         | 
| 191 | 
            -
                  quote + scan_to_delimiter
         | 
| 192 | 
            -
                else
         | 
| 185 | 
            +
                if (value = scan_to_quote quote)
         | 
| 193 186 | 
             
                  @scanner.get_byte
         | 
| 194 | 
            -
                  value.gsub | 
| 187 | 
            +
                  value.gsub EscapedQuoteRxs[quote], quote
         | 
| 188 | 
            +
                else
         | 
| 189 | 
            +
                  %(#{quote}#{scan_to_delimiter})
         | 
| 195 190 | 
             
                end
         | 
| 196 191 | 
             
              end
         | 
| 197 192 |  | 
| 198 193 | 
             
              def skip_blank
         | 
| 199 | 
            -
                @scanner.skip  | 
| 194 | 
            +
                @scanner.skip BlankRx
         | 
| 200 195 | 
             
              end
         | 
| 201 196 |  | 
| 202 197 | 
             
              def skip_delimiter
         | 
| 203 | 
            -
                @scanner.skip  | 
| 198 | 
            +
                @scanner.skip @delimiter_skip_pattern
         | 
| 204 199 | 
             
              end
         | 
| 205 200 |  | 
| 206 201 | 
             
              def scan_name
         | 
| 207 | 
            -
                @scanner.scan  | 
| 202 | 
            +
                @scanner.scan NameRx
         | 
| 208 203 | 
             
              end
         | 
| 209 204 |  | 
| 210 205 | 
             
              def scan_to_delimiter
         | 
| 211 | 
            -
                @scanner.scan  | 
| 206 | 
            +
                @scanner.scan @delimiter_boundary_pattern
         | 
| 212 207 | 
             
              end
         | 
| 213 208 |  | 
| 214 | 
            -
              def scan_to_quote | 
| 215 | 
            -
                @scanner.scan  | 
| 209 | 
            +
              def scan_to_quote quote
         | 
| 210 | 
            +
                @scanner.scan BoundaryRxs[quote]
         | 
| 216 211 | 
             
              end
         | 
| 217 212 |  | 
| 218 213 | 
             
            end
         | 
    
        data/lib/asciidoctor/block.rb
    CHANGED
    
    | @@ -8,6 +8,20 @@ module Asciidoctor | |
| 8 8 | 
             
            #   => "<em>This</em> is a <test>"
         | 
| 9 9 | 
             
            class Block < AbstractBlock
         | 
| 10 10 |  | 
| 11 | 
            +
              DEFAULT_CONTENT_MODEL = ::Hash.new(:simple).merge({
         | 
| 12 | 
            +
                # TODO should probably fill in all known blocks
         | 
| 13 | 
            +
                :audio => :empty,
         | 
| 14 | 
            +
                :image => :empty,
         | 
| 15 | 
            +
                :listing => :verbatim,
         | 
| 16 | 
            +
                :literal => :verbatim,
         | 
| 17 | 
            +
                :stem => :raw,
         | 
| 18 | 
            +
                :open => :compound,
         | 
| 19 | 
            +
                :page_break => :empty,
         | 
| 20 | 
            +
                :pass => :raw,
         | 
| 21 | 
            +
                :thematic_break => :empty,
         | 
| 22 | 
            +
                :video => :empty
         | 
| 23 | 
            +
              })
         | 
| 24 | 
            +
             | 
| 11 25 | 
             
              # Public: Create alias for context to be consistent w/ AsciiDoc
         | 
| 12 26 | 
             
              alias :blockname :context
         | 
| 13 27 |  | 
| @@ -25,31 +39,31 @@ class Block < AbstractBlock | |
| 25 39 | 
             
              #                 * :source a String or Array of raw source for this Block. (default: nil)
         | 
| 26 40 | 
             
              #--
         | 
| 27 41 | 
             
              # QUESTION should we store source_data as lines for blocks that have compound content models?
         | 
| 28 | 
            -
              def initialize | 
| 29 | 
            -
                super | 
| 30 | 
            -
                @content_model = opts | 
| 31 | 
            -
                 | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
                  if ::Asciidoctor::FORCE_ENCODING
         | 
| 39 | 
            -
                    @lines = raw_source.lines.map {|line| "#{line.rstrip.force_encoding(::Encoding::UTF_8)}\n" }
         | 
| 42 | 
            +
              def initialize parent, context, opts = {}
         | 
| 43 | 
            +
                super
         | 
| 44 | 
            +
                @content_model = opts[:content_model] || DEFAULT_CONTENT_MODEL[context]
         | 
| 45 | 
            +
                if opts.has_key? :subs
         | 
| 46 | 
            +
                  # FIXME this is a bit funky
         | 
| 47 | 
            +
                  # we have to be defensive to avoid lock_in_subs wiping out the override
         | 
| 48 | 
            +
                  if !(subs = opts[:subs]) || (subs.is_a? ::Array)
         | 
| 49 | 
            +
                    @subs = subs || []
         | 
| 50 | 
            +
                    @default_subs = @subs.dup
         | 
| 51 | 
            +
                    @attributes.delete('subs')
         | 
| 40 52 | 
             
                  else
         | 
| 41 | 
            -
                    @ | 
| 42 | 
            -
                  end
         | 
| 43 | 
            -
                  if (last = @lines.pop)
         | 
| 44 | 
            -
                    @lines.push last.chomp
         | 
| 53 | 
            +
                    @attributes['subs'] = %(#{subs})
         | 
| 45 54 | 
             
                  end
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
                if !(raw_source = opts[:source])
         | 
| 57 | 
            +
                  @lines = []
         | 
| 58 | 
            +
                elsif raw_source.is_a? ::String
         | 
| 59 | 
            +
                  @lines = Helpers.normalize_lines_from_string raw_source
         | 
| 46 60 | 
             
                else
         | 
| 47 61 | 
             
                  @lines = raw_source.dup
         | 
| 48 62 | 
             
                end
         | 
| 49 63 | 
             
              end
         | 
| 50 64 |  | 
| 51 | 
            -
              # Public: Get  | 
| 52 | 
            -
              #  | 
| 65 | 
            +
              # Public: Get the converted result of the child blocks by converting the
         | 
| 66 | 
            +
              # children appropriate to content model that this block supports.
         | 
| 53 67 | 
             
              #
         | 
| 54 68 | 
             
              # Examples
         | 
| 55 69 | 
             
              #
         | 
| @@ -62,10 +76,23 @@ class Block < AbstractBlock | |
| 62 76 | 
             
                case @content_model
         | 
| 63 77 | 
             
                when :compound
         | 
| 64 78 | 
             
                  super
         | 
| 65 | 
            -
                when :simple | 
| 66 | 
            -
                  apply_subs | 
| 79 | 
            +
                when :simple
         | 
| 80 | 
            +
                  apply_subs(@lines * EOL, @subs)
         | 
| 81 | 
            +
                when :verbatim, :raw
         | 
| 82 | 
            +
                  #((apply_subs @lines.join(EOL), @subs).sub StripLineWiseRx, '\1')
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                  # QUESTION could we use strip here instead of popping empty lines?
         | 
| 85 | 
            +
                  # maybe apply_subs can know how to strip whitespace?
         | 
| 86 | 
            +
                  result = apply_subs @lines, @subs
         | 
| 87 | 
            +
                  if result.size < 2
         | 
| 88 | 
            +
                    result[0]
         | 
| 89 | 
            +
                  else
         | 
| 90 | 
            +
                    result.shift while (first = result[0]) && first.rstrip.empty?
         | 
| 91 | 
            +
                    result.pop while (last = result[-1]) && last.rstrip.empty?
         | 
| 92 | 
            +
                    result * EOL
         | 
| 93 | 
            +
                  end
         | 
| 67 94 | 
             
                else
         | 
| 68 | 
            -
                  warn  | 
| 95 | 
            +
                  warn %(Unknown content model '#{@content_model}' for block: #{to_s}) unless @content_model == :empty
         | 
| 69 96 | 
             
                  nil
         | 
| 70 97 | 
             
                end
         | 
| 71 98 | 
             
              end
         | 
| @@ -75,12 +102,12 @@ class Block < AbstractBlock | |
| 75 102 | 
             
              # Returns the a String containing the lines joined together or nil if there
         | 
| 76 103 | 
             
              # are no lines
         | 
| 77 104 | 
             
              def source
         | 
| 78 | 
            -
                @lines | 
| 105 | 
            +
                @lines * EOL
         | 
| 79 106 | 
             
              end
         | 
| 80 107 |  | 
| 81 108 | 
             
              def to_s
         | 
| 82 | 
            -
                content_summary = @content_model == :compound ? %( | 
| 83 | 
            -
                %( | 
| 109 | 
            +
                content_summary = @content_model == :compound ? %(blocks: #{@blocks.size}) : %(lines: #{@lines.size})
         | 
| 110 | 
            +
                %(#<#{self.class}@#{object_id} {context: #{@context.inspect}, content_model: #{@content_model.inspect}, style: #{@style.inspect}, #{content_summary}}>)
         | 
| 84 111 | 
             
              end
         | 
| 85 112 | 
             
            end
         | 
| 86 113 | 
             
            end
         | 
    
        data/lib/asciidoctor/callouts.rb
    CHANGED
    
    | @@ -36,8 +36,8 @@ class Callouts | |
| 36 36 | 
             
              # Public: Get the next callout index in the document
         | 
| 37 37 | 
             
              #
         | 
| 38 38 | 
             
              # Reads the next callout index in the document and advances the pointer.
         | 
| 39 | 
            -
              # This method is used during  | 
| 40 | 
            -
              # callout that was generated during  | 
| 39 | 
            +
              # This method is used during conversion to retrieve the unique id of the
         | 
| 40 | 
            +
              # callout that was generated during parsing.
         | 
| 41 41 | 
             
              #
         | 
| 42 42 | 
             
              # Returns The unique String id of the next callout in the document
         | 
| 43 43 | 
             
              def read_next_id
         | 
| @@ -60,10 +60,7 @@ class Callouts | |
| 60 60 | 
             
              #
         | 
| 61 61 | 
             
              # Returns A space-separated String of callout ids associated with the specified list item
         | 
| 62 62 | 
             
              def callout_ids(li_ordinal)
         | 
| 63 | 
            -
                current_list. | 
| 64 | 
            -
                  collector << element[:id] if element[:ordinal] == li_ordinal
         | 
| 65 | 
            -
                  collector
         | 
| 66 | 
            -
                } * ' '
         | 
| 63 | 
            +
                current_list.map {|element| element[:ordinal] == li_ordinal ? %(#{element[:id]} ) : nil }.join.chop
         | 
| 67 64 | 
             
              end
         | 
| 68 65 |  | 
| 69 66 | 
             
              # Public: The current list for which callouts are being collected
         | 
| @@ -89,7 +86,7 @@ class Callouts | |
| 89 86 | 
             
              end
         | 
| 90 87 |  | 
| 91 88 | 
             
              # Public: Rewind the list index pointer, intended to be used when switching
         | 
| 92 | 
            -
              # from the parsing to  | 
| 89 | 
            +
              # from the parsing to conversion phase.
         | 
| 93 90 | 
             
              #
         | 
| 94 91 | 
             
              # Returns nothing
         | 
| 95 92 | 
             
              def rewind
         | 
| @@ -12,100 +12,110 @@ module Asciidoctor | |
| 12 12 | 
             
                    @err = nil
         | 
| 13 13 | 
             
                    @code = 0
         | 
| 14 14 | 
             
                    options = options.flatten
         | 
| 15 | 
            -
                    if  | 
| 16 | 
            -
                      @options =  | 
| 17 | 
            -
                    elsif  | 
| 15 | 
            +
                    if (first_option = options[0]).is_a?(Cli::Options)
         | 
| 16 | 
            +
                      @options = first_option
         | 
| 17 | 
            +
                    elsif first_option.is_a?(::Hash)
         | 
| 18 18 | 
             
                      @options = Cli::Options.new(options)
         | 
| 19 19 | 
             
                    else
         | 
| 20 | 
            -
                       | 
| 21 | 
            -
             | 
| 22 | 
            -
                      if @options.is_a?(Integer)
         | 
| 23 | 
            -
                        @code = @options
         | 
| 20 | 
            +
                      if (result = Cli::Options.parse! options).is_a? ::Integer
         | 
| 21 | 
            +
                        @code = result
         | 
| 24 22 | 
             
                        @options = nil
         | 
| 23 | 
            +
                      else
         | 
| 24 | 
            +
                        @options = result
         | 
| 25 25 | 
             
                      end
         | 
| 26 26 | 
             
                    end
         | 
| 27 27 | 
             
                  end
         | 
| 28 28 |  | 
| 29 29 | 
             
                  def invoke!
         | 
| 30 | 
            -
                     | 
| 31 | 
            -
             | 
| 32 | 
            -
                    begin
         | 
| 33 | 
            -
                      opts = {}
         | 
| 34 | 
            -
                      profile = false
         | 
| 35 | 
            -
                      infiles = []
         | 
| 36 | 
            -
                      outfile = nil
         | 
| 37 | 
            -
                      tofile = nil
         | 
| 38 | 
            -
                      @options.map {|k, v|
         | 
| 39 | 
            -
                        case k
         | 
| 40 | 
            -
                        when :input_files
         | 
| 41 | 
            -
                          infiles = v
         | 
| 42 | 
            -
                        when :output_file
         | 
| 43 | 
            -
                          outfile = v
         | 
| 44 | 
            -
                        when :destination_dir
         | 
| 45 | 
            -
                          #opts[:to_dir] = File.expand_path(v) unless v.nil?
         | 
| 46 | 
            -
                          opts[:to_dir] = v unless v.nil?
         | 
| 47 | 
            -
                        when :attributes
         | 
| 48 | 
            -
                          opts[:attributes] = v.dup
         | 
| 49 | 
            -
                        when :verbose
         | 
| 50 | 
            -
                          profile = true if v
         | 
| 51 | 
            -
                        when :trace
         | 
| 52 | 
            -
                          # currently, nothing
         | 
| 53 | 
            -
                        else
         | 
| 54 | 
            -
                          opts[k] = v unless v.nil?
         | 
| 55 | 
            -
                        end
         | 
| 56 | 
            -
                      }
         | 
| 30 | 
            +
                    old_verbose = -1
         | 
| 31 | 
            +
                    return unless @options
         | 
| 57 32 |  | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
                       | 
| 62 | 
            -
             | 
| 63 | 
            -
                       | 
| 33 | 
            +
                    old_verbose = $VERBOSE
         | 
| 34 | 
            +
                    case @options[:verbose]
         | 
| 35 | 
            +
                    when 0
         | 
| 36 | 
            +
                      $VERBOSE = nil
         | 
| 37 | 
            +
                    when 1
         | 
| 38 | 
            +
                      $VERBOSE = false
         | 
| 39 | 
            +
                    when 2
         | 
| 40 | 
            +
                      $VERBOSE = true
         | 
| 41 | 
            +
                    end
         | 
| 64 42 |  | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 69 | 
            -
             | 
| 70 | 
            -
             | 
| 43 | 
            +
                    opts = {}
         | 
| 44 | 
            +
                    infiles = []
         | 
| 45 | 
            +
                    outfile = nil
         | 
| 46 | 
            +
                    tofile = nil
         | 
| 47 | 
            +
                    @options.map do |key, val|
         | 
| 48 | 
            +
                      case key
         | 
| 49 | 
            +
                      when :input_files
         | 
| 50 | 
            +
                        infiles = val
         | 
| 51 | 
            +
                      when :output_file
         | 
| 52 | 
            +
                        outfile = val
         | 
| 53 | 
            +
                      when :destination_dir
         | 
| 54 | 
            +
                        opts[:to_dir] = val if val
         | 
| 55 | 
            +
                      when :attributes
         | 
| 56 | 
            +
                        # NOTE processor will dup attributes internally
         | 
| 57 | 
            +
                        opts[:attributes] = val
         | 
| 58 | 
            +
                      when :trace
         | 
| 59 | 
            +
                        # currently, nothing
         | 
| 71 60 | 
             
                      else
         | 
| 72 | 
            -
                         | 
| 73 | 
            -
                        # automatically calculate outfile based on infile
         | 
| 74 | 
            -
                        opts[:in_place] = true unless opts.has_key? :to_dir
         | 
| 75 | 
            -
                        opts[:mkdirs] = true
         | 
| 61 | 
            +
                        opts[key] = val unless val.nil?
         | 
| 76 62 | 
             
                      end
         | 
| 63 | 
            +
                    end
         | 
| 77 64 |  | 
| 78 | 
            -
             | 
| 79 | 
            -
                       | 
| 80 | 
            -
             | 
| 81 | 
            -
             | 
| 82 | 
            -
             | 
| 83 | 
            -
             | 
| 65 | 
            +
                    if infiles.size == 1 && infiles[0] == '-'
         | 
| 66 | 
            +
                      # allows use of block to supply stdin, particularly useful for tests
         | 
| 67 | 
            +
                      inputs = [block_given? ? yield : STDIN]
         | 
| 68 | 
            +
                    else
         | 
| 69 | 
            +
                      inputs = infiles.map {|infile| ::File.new infile, 'r'}
         | 
| 70 | 
            +
                    end
         | 
| 84 71 |  | 
| 85 | 
            -
             | 
| 86 | 
            -
             | 
| 72 | 
            +
                    # NOTE if infile is stdin, default to outfile as stout
         | 
| 73 | 
            +
                    if outfile == '-' || (!outfile && infiles.size == 1 && infiles[0] == '-')
         | 
| 74 | 
            +
                      tofile = (@out || $stdout)
         | 
| 75 | 
            +
                    elsif outfile
         | 
| 76 | 
            +
                      tofile = outfile
         | 
| 77 | 
            +
                      opts[:mkdirs] = true
         | 
| 78 | 
            +
                    else
         | 
| 79 | 
            +
                      # automatically calculate outfile based on infile unless to_dir is set
         | 
| 80 | 
            +
                      tofile = nil
         | 
| 81 | 
            +
                      opts[:mkdirs] = true
         | 
| 82 | 
            +
                    end
         | 
| 87 83 |  | 
| 88 | 
            -
             | 
| 89 | 
            -
             | 
| 90 | 
            -
             | 
| 91 | 
            -
             | 
| 92 | 
            -
             | 
| 93 | 
            -
             | 
| 94 | 
            -
             | 
| 95 | 
            -
                         | 
| 84 | 
            +
                    show_timings = @options[:timings]
         | 
| 85 | 
            +
                    inputs.each do |input|
         | 
| 86 | 
            +
                      # NOTE processor will dup options and attributes internally
         | 
| 87 | 
            +
                      input_opts = tofile.nil? ? opts : opts.merge(:to_file => tofile)
         | 
| 88 | 
            +
                      if show_timings
         | 
| 89 | 
            +
                        timings = Timings.new
         | 
| 90 | 
            +
                        @documents << ::Asciidoctor.convert(input, input_opts.merge(:timings => timings))
         | 
| 91 | 
            +
                        timings.print_report((@err || $stderr), ((input.respond_to? :path) ? input.path : '-'))
         | 
| 92 | 
            +
                      else
         | 
| 93 | 
            +
                        @documents << ::Asciidoctor.convert(input, input_opts)
         | 
| 94 | 
            +
                      end
         | 
| 95 | 
            +
                    end
         | 
| 96 | 
            +
                  rescue ::Exception => e
         | 
| 97 | 
            +
                    if ::SignalException === e
         | 
| 98 | 
            +
                      @code = e.signo
         | 
| 99 | 
            +
                      # add extra endline if Ctrl+C is used
         | 
| 100 | 
            +
                      (@err || $stderr).puts if ::Interrupt === e
         | 
| 101 | 
            +
                    else
         | 
| 102 | 
            +
                      @code = (e.respond_to? :status) ? e.status : 1
         | 
| 103 | 
            +
                      if @options[:trace]
         | 
| 104 | 
            +
                        raise e
         | 
| 105 | 
            +
                      else
         | 
| 106 | 
            +
                        err = (@err || $stderr)
         | 
| 107 | 
            +
                        err.print %(#{e.class}: ) if ::RuntimeError === e
         | 
| 108 | 
            +
                        err.puts e.message
         | 
| 109 | 
            +
                        err.puts '  Use --trace for backtrace'
         | 
| 96 110 | 
             
                      end
         | 
| 97 | 
            -
                    rescue Exception => e
         | 
| 98 | 
            -
                      raise e if @options[:trace] || SystemExit === e
         | 
| 99 | 
            -
                      err = (@err || $stderr)
         | 
| 100 | 
            -
                      err.print "#{e.class}: " if e.class != RuntimeError
         | 
| 101 | 
            -
                      err.puts e.message
         | 
| 102 | 
            -
                      err.puts '  Use --trace for backtrace'
         | 
| 103 | 
            -
                      @code = 1
         | 
| 104 111 | 
             
                    end
         | 
| 112 | 
            +
                    nil
         | 
| 113 | 
            +
                  ensure
         | 
| 114 | 
            +
                    $VERBOSE = old_verbose unless old_verbose == -1
         | 
| 105 115 | 
             
                  end
         | 
| 106 116 |  | 
| 107 117 | 
             
                  def document
         | 
| 108 | 
            -
                    @documents | 
| 118 | 
            +
                    @documents[0]
         | 
| 109 119 | 
             
                  end
         | 
| 110 120 |  | 
| 111 121 | 
             
                  def redirect_streams(out, err = nil)
         | 
| @@ -114,11 +124,11 @@ module Asciidoctor | |
| 114 124 | 
             
                  end
         | 
| 115 125 |  | 
| 116 126 | 
             
                  def read_output
         | 
| 117 | 
            -
                     | 
| 127 | 
            +
                    @out ? @out.string : ''
         | 
| 118 128 | 
             
                  end
         | 
| 119 129 |  | 
| 120 130 | 
             
                  def read_error
         | 
| 121 | 
            -
                     | 
| 131 | 
            +
                    @err ? @err.string : ''
         | 
| 122 132 | 
             
                  end
         | 
| 123 133 |  | 
| 124 134 | 
             
                  def reset_streams
         |