asciidoctor 1.5.5 → 1.5.6
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 +216 -1
- data/CONTRIBUTING.adoc +2 -2
- data/Gemfile +20 -1
- data/LICENSE.adoc +1 -1
- data/README-fr.adoc +4 -3
- data/README-jp.adoc +11 -10
- data/README-zh_CN.adoc +4 -3
- data/README.adoc +17 -202
- data/Rakefile +41 -25
- data/asciidoctor.gemspec +9 -10
- data/data/locale/attributes.adoc +216 -34
- data/data/stylesheets/asciidoctor-default.css +23 -16
- data/features/step_definitions.rb +15 -19
- data/features/xref.feature +584 -20
- data/lib/asciidoctor.rb +292 -278
- data/lib/asciidoctor/abstract_block.rb +155 -94
- data/lib/asciidoctor/abstract_node.rb +108 -94
- data/lib/asciidoctor/attribute_list.rb +30 -22
- data/lib/asciidoctor/block.rb +7 -7
- data/lib/asciidoctor/cli/invoker.rb +47 -34
- data/lib/asciidoctor/cli/options.rb +22 -11
- data/lib/asciidoctor/converter.rb +3 -3
- data/lib/asciidoctor/converter/base.rb +2 -2
- data/lib/asciidoctor/converter/composite.rb +1 -1
- data/lib/asciidoctor/converter/docbook45.rb +2 -2
- data/lib/asciidoctor/converter/docbook5.rb +132 -87
- data/lib/asciidoctor/converter/factory.rb +0 -1
- data/lib/asciidoctor/converter/html5.rb +116 -98
- data/lib/asciidoctor/converter/manpage.rb +51 -52
- data/lib/asciidoctor/converter/template.rb +47 -36
- data/lib/asciidoctor/core_ext.rb +8 -2
- data/lib/asciidoctor/core_ext/1.8.7/hash/key.rb +4 -0
- data/lib/asciidoctor/core_ext/1.8.7/io/binread.rb +6 -0
- data/lib/asciidoctor/core_ext/1.8.7/io/write.rb +5 -0
- data/lib/asciidoctor/core_ext/1.8.7/string/chr.rb +1 -1
- data/lib/asciidoctor/core_ext/1.8.7/string/{limit.rb → limit_bytesize.rb} +7 -6
- data/lib/asciidoctor/core_ext/1.8.7/symbol/empty.rb +6 -0
- data/lib/asciidoctor/core_ext/1.8.7/symbol/length.rb +1 -1
- data/lib/asciidoctor/core_ext/nil_or_empty.rb +5 -5
- data/lib/asciidoctor/core_ext/regexp/is_match.rb +3 -0
- data/lib/asciidoctor/core_ext/string/{limit.rb → limit_bytesize.rb} +2 -2
- data/lib/asciidoctor/document.rb +216 -213
- data/lib/asciidoctor/extensions.rb +318 -185
- data/lib/asciidoctor/helpers.rb +35 -35
- data/lib/asciidoctor/inline.rb +32 -1
- data/lib/asciidoctor/list.rb +22 -6
- data/lib/asciidoctor/parser.rb +1008 -1038
- data/lib/asciidoctor/path_resolver.rb +46 -50
- data/lib/asciidoctor/reader.rb +275 -251
- data/lib/asciidoctor/section.rb +86 -58
- data/lib/asciidoctor/stylesheets.rb +6 -6
- data/lib/asciidoctor/substitutors.rb +567 -649
- data/lib/asciidoctor/table.rb +163 -108
- data/lib/asciidoctor/version.rb +1 -1
- data/man/asciidoctor.1 +18 -16
- data/man/asciidoctor.adoc +15 -13
- data/test/attributes_test.rb +138 -22
- data/test/blocks_test.rb +377 -97
- data/test/converter_test.rb +13 -0
- data/test/document_test.rb +244 -34
- data/test/extensions_test.rb +409 -42
- data/test/fixtures/asciidoc_index.txt +521 -0
- data/test/fixtures/basic-docinfo-footer.html +6 -0
- data/test/fixtures/basic-docinfo-footer.xml +8 -0
- data/test/fixtures/basic-docinfo.html +1 -0
- data/test/fixtures/basic-docinfo.xml +4 -0
- data/test/fixtures/basic.asciidoc +5 -0
- data/test/fixtures/chapter-a.adoc +3 -0
- data/test/fixtures/child-include.adoc +5 -0
- data/test/fixtures/circle.svg +9 -0
- data/test/fixtures/custom-backends/erb/html5/block_paragraph.html.erb +6 -0
- data/test/fixtures/custom-backends/haml/docbook45/block_paragraph.xml.haml +6 -0
- data/test/fixtures/custom-backends/haml/html5-tweaks/block_paragraph.html.haml +1 -0
- data/test/fixtures/custom-backends/haml/html5/block_paragraph.html.haml +3 -0
- data/test/fixtures/custom-backends/haml/html5/block_sidebar.html.haml +5 -0
- data/test/fixtures/custom-backends/slim/docbook45/block_paragraph.xml.slim +6 -0
- data/test/fixtures/custom-backends/slim/html5/block_paragraph.html.slim +3 -0
- data/test/fixtures/custom-backends/slim/html5/block_sidebar.html.slim +5 -0
- data/test/fixtures/custom-docinfodir/basic-docinfo.html +1 -0
- data/test/fixtures/custom-docinfodir/docinfo.html +1 -0
- data/test/fixtures/docinfo-footer.html +1 -0
- data/test/fixtures/docinfo-footer.xml +9 -0
- data/test/fixtures/docinfo.html +1 -0
- data/test/fixtures/docinfo.xml +3 -0
- data/test/fixtures/dot.gif +0 -0
- data/test/fixtures/encoding.asciidoc +13 -0
- data/test/fixtures/grandchild-include.adoc +3 -0
- data/test/fixtures/hello-asciidoctor.pdf +69 -0
- data/test/fixtures/include-file.asciidoc +24 -0
- data/test/fixtures/include-file.ml +3 -0
- data/test/fixtures/include-file.xml +5 -0
- data/test/fixtures/master.adoc +5 -0
- data/test/fixtures/mismatched-end-tag.adoc +7 -0
- data/test/fixtures/parent-include-restricted.adoc +5 -0
- data/test/fixtures/parent-include.adoc +5 -0
- data/test/fixtures/sample.asciidoc +26 -0
- data/test/fixtures/stylesheets/custom.css +3 -0
- data/test/fixtures/subs-docinfo.html +2 -0
- data/test/fixtures/subs.adoc +7 -0
- data/test/fixtures/tagged-class-enclosed.rb +26 -0
- data/test/fixtures/tagged-class.rb +23 -0
- data/test/fixtures/tip.gif +0 -0
- data/test/invoker_test.rb +82 -4
- data/test/links_test.rb +312 -37
- data/test/lists_test.rb +204 -25
- data/test/manpage_test.rb +191 -4
- data/test/options_test.rb +18 -1
- data/test/paragraphs_test.rb +32 -7
- data/test/parser_test.rb +150 -30
- data/test/paths_test.rb +47 -13
- data/test/preamble_test.rb +1 -1
- data/test/reader_test.rb +366 -126
- data/test/sections_test.rb +203 -56
- data/test/substitutions_test.rb +339 -131
- data/test/tables_test.rb +315 -15
- data/test/test_helper.rb +400 -0
- data/test/text_test.rb +5 -5
- metadata +110 -22
    
        data/lib/asciidoctor/table.rb
    CHANGED
    
    | @@ -14,50 +14,20 @@ class Table < AbstractBlock | |
| 14 14 | 
             
                  @body = body
         | 
| 15 15 | 
             
                end
         | 
| 16 16 |  | 
| 17 | 
            -
                alias  | 
| 17 | 
            +
                alias [] send
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                # Public: Returns the rows grouped by section.
         | 
| 20 | 
            +
                #
         | 
| 21 | 
            +
                # Creates a 2-dimensional array of two element entries. The first element
         | 
| 22 | 
            +
                # is the section name as a symbol. The second element is the Array of rows
         | 
| 23 | 
            +
                # in that section. The entries are in document order (head, foot, body).
         | 
| 24 | 
            +
                #
         | 
| 25 | 
            +
                # Returns a 2-dimentional Array of rows grouped by section.
         | 
| 26 | 
            +
                def by_section
         | 
| 27 | 
            +
                  [[:head, @head], [:foot, @foot], [:body, @body]]
         | 
| 28 | 
            +
                end
         | 
| 18 29 | 
             
              end
         | 
| 19 30 |  | 
| 20 | 
            -
              # Public: A String key that specifies the default table format in AsciiDoc (psv)
         | 
| 21 | 
            -
              DEFAULT_DATA_FORMAT = 'psv'
         | 
| 22 | 
            -
             | 
| 23 | 
            -
              # Public: An Array of String keys that represent the table formats in AsciiDoc
         | 
| 24 | 
            -
              DATA_FORMATS = ['psv', 'dsv', 'csv']
         | 
| 25 | 
            -
             | 
| 26 | 
            -
              # Public: A Hash mapping the AsciiDoc table formats to their default delimiters
         | 
| 27 | 
            -
              DEFAULT_DELIMITERS = {
         | 
| 28 | 
            -
                'psv' => '|',
         | 
| 29 | 
            -
                'dsv' => ':',
         | 
| 30 | 
            -
                'csv' => ','
         | 
| 31 | 
            -
              }
         | 
| 32 | 
            -
             | 
| 33 | 
            -
              # Public: A Hash mapping styles abbreviations to styles that can be applied
         | 
| 34 | 
            -
              # to a table column or cell
         | 
| 35 | 
            -
              TEXT_STYLES = {
         | 
| 36 | 
            -
                'd' => :none,
         | 
| 37 | 
            -
                's' => :strong,
         | 
| 38 | 
            -
                'e' => :emphasis,
         | 
| 39 | 
            -
                'm' => :monospaced,
         | 
| 40 | 
            -
                'h' => :header,
         | 
| 41 | 
            -
                'l' => :literal,
         | 
| 42 | 
            -
                'v' => :verse,
         | 
| 43 | 
            -
                'a' => :asciidoc
         | 
| 44 | 
            -
              }
         | 
| 45 | 
            -
             | 
| 46 | 
            -
              # Public: A Hash mapping alignment abbreviations to alignments (horizontal
         | 
| 47 | 
            -
              # and vertial) that can be applies to a table column or cell
         | 
| 48 | 
            -
              ALIGNMENTS = {
         | 
| 49 | 
            -
                :h => {
         | 
| 50 | 
            -
                  '<' => 'left',
         | 
| 51 | 
            -
                  '>' => 'right',
         | 
| 52 | 
            -
                  '^' => 'center'
         | 
| 53 | 
            -
                },
         | 
| 54 | 
            -
                :v => {
         | 
| 55 | 
            -
                  '<' => 'top',
         | 
| 56 | 
            -
                  '>' => 'bottom',
         | 
| 57 | 
            -
                  '^' => 'middle'
         | 
| 58 | 
            -
                }
         | 
| 59 | 
            -
              }
         | 
| 60 | 
            -
             | 
| 61 31 | 
             
              # Public: Get/Set the columns for this table
         | 
| 62 32 | 
             
              attr_accessor :columns
         | 
| 63 33 |  | 
| @@ -68,6 +38,9 @@ class Table < AbstractBlock | |
| 68 38 | 
             
              # Public: Boolean specifies whether this table has a header row
         | 
| 69 39 | 
             
              attr_accessor :has_header_option
         | 
| 70 40 |  | 
| 41 | 
            +
              # Public: Get the caption for this table
         | 
| 42 | 
            +
              attr_reader :caption
         | 
| 43 | 
            +
             | 
| 71 44 | 
             
              def initialize parent, attributes
         | 
| 72 45 | 
             
                super parent, :table
         | 
| 73 46 | 
             
                @rows = Rows.new
         | 
| @@ -87,6 +60,7 @@ class Table < AbstractBlock | |
| 87 60 | 
             
                @attributes['tablepcwidth'] = pcwidth_intval
         | 
| 88 61 |  | 
| 89 62 | 
             
                if @document.attributes.key? 'pagewidth'
         | 
| 63 | 
            +
                  # FIXME calculate more accurately (only used in DocBook output)
         | 
| 90 64 | 
             
                  @attributes['tableabswidth'] ||=
         | 
| 91 65 | 
             
                      ((@attributes['tablepcwidth'].to_f / 100) * @document.attributes['pagewidth']).round
         | 
| 92 66 | 
             
                end
         | 
| @@ -135,12 +109,22 @@ class Table < AbstractBlock | |
| 135 109 | 
             
                  @columns.each {|col| total_width += (col_pcwidth = col.assign_width nil, width_base, pf) }
         | 
| 136 110 | 
             
                else
         | 
| 137 111 | 
             
                  col_pcwidth = ((100 * pf / @columns.size).to_i) / pf
         | 
| 112 | 
            +
                  # or...
         | 
| 113 | 
            +
                  #col_pcwidth = (100.0 / @columns.size).truncate 4
         | 
| 138 114 | 
             
                  col_pcwidth = col_pcwidth.to_i if col_pcwidth.to_i == col_pcwidth
         | 
| 139 115 | 
             
                  @columns.each {|col| total_width += col.assign_width col_pcwidth }
         | 
| 140 116 | 
             
                end
         | 
| 141 117 |  | 
| 142 | 
            -
                # donate balance, if any, to final column
         | 
| 143 | 
            -
                 | 
| 118 | 
            +
                # donate balance, if any, to final column (using half up rounding)
         | 
| 119 | 
            +
                unless total_width == 100
         | 
| 120 | 
            +
                  @columns[-1].assign_width(((100 - total_width + col_pcwidth) * pf).round / pf)
         | 
| 121 | 
            +
                  # or (manual half up rounding)...
         | 
| 122 | 
            +
                  #numerator = (raw_numerator = (100 - total_width + col_pcwidth) * pf).to_i
         | 
| 123 | 
            +
                  #numerator += 1 if raw_numerator >= numerator + 0.5
         | 
| 124 | 
            +
                  #@columns[-1].assign_width numerator / pf
         | 
| 125 | 
            +
                  # or...
         | 
| 126 | 
            +
                  #@columns[-1].assign_width((100 - total_width + col_pcwidth).round 4)
         | 
| 127 | 
            +
                end
         | 
| 144 128 |  | 
| 145 129 | 
             
                nil
         | 
| 146 130 | 
             
              end
         | 
| @@ -149,7 +133,7 @@ class Table < AbstractBlock | |
| 149 133 | 
             
              # by the options on the table
         | 
| 150 134 | 
             
              #
         | 
| 151 135 | 
             
              # returns nothing
         | 
| 152 | 
            -
              def partition_header_footer( | 
| 136 | 
            +
              def partition_header_footer(attrs)
         | 
| 153 137 | 
             
                # set rowcount before splitting up body rows
         | 
| 154 138 | 
             
                @attributes['rowcount'] = @rows.body.size
         | 
| 155 139 |  | 
| @@ -164,7 +148,7 @@ class Table < AbstractBlock | |
| 164 148 | 
             
                  @rows.head = [head]
         | 
| 165 149 | 
             
                end
         | 
| 166 150 |  | 
| 167 | 
            -
                if num_body_rows > 0 &&  | 
| 151 | 
            +
                if num_body_rows > 0 && attrs.key?('footer-option')
         | 
| 168 152 | 
             
                  @rows.foot = [@rows.body.pop]
         | 
| 169 153 | 
             
                end
         | 
| 170 154 |  | 
| @@ -189,7 +173,7 @@ class Table::Column < AbstractNode | |
| 189 173 | 
             
              end
         | 
| 190 174 |  | 
| 191 175 | 
             
              # Public: An alias to the parent block (which is always a Table)
         | 
| 192 | 
            -
              alias  | 
| 176 | 
            +
              alias table parent
         | 
| 193 177 |  | 
| 194 178 | 
             
              # Internal: Calculate and assign the widths (percentage and absolute) for this column
         | 
| 195 179 | 
             
              #
         | 
| @@ -199,6 +183,8 @@ class Table::Column < AbstractNode | |
| 199 183 | 
             
              def assign_width col_pcwidth, width_base = nil, pf = 10000.0
         | 
| 200 184 | 
             
                if width_base
         | 
| 201 185 | 
             
                  col_pcwidth = ((@attributes['width'].to_f / width_base) * 100 * pf).to_i / pf
         | 
| 186 | 
            +
                  # or...
         | 
| 187 | 
            +
                  #col_pcwidth = (@attributes['width'].to_f * 100.0 / width_base).truncate 4
         | 
| 202 188 | 
             
                  col_pcwidth = col_pcwidth.to_i if col_pcwidth.to_i == col_pcwidth
         | 
| 203 189 | 
             
                end
         | 
| 204 190 | 
             
                @attributes['colpcwidth'] = col_pcwidth
         | 
| @@ -222,58 +208,90 @@ class Table::Cell < AbstractNode | |
| 222 208 | 
             
              attr_accessor :rowspan
         | 
| 223 209 |  | 
| 224 210 | 
             
              # Public: An alias to the parent block (which is always a Column)
         | 
| 225 | 
            -
              alias  | 
| 211 | 
            +
              alias column parent
         | 
| 226 212 |  | 
| 227 213 | 
             
              # Public: The internal Asciidoctor::Document for a cell that has the asciidoc style
         | 
| 228 214 | 
             
              attr_reader :inner_document
         | 
| 229 215 |  | 
| 230 | 
            -
              def initialize column,  | 
| 216 | 
            +
              def initialize column, cell_text, attributes = {}, opts = {}
         | 
| 231 217 | 
             
                super column, :cell
         | 
| 232 | 
            -
                @text = text
         | 
| 233 | 
            -
                @style = nil
         | 
| 234 | 
            -
                @colspan = nil
         | 
| 235 | 
            -
                @rowspan = nil
         | 
| 236 | 
            -
                # TODO feels hacky
         | 
| 237 218 | 
             
                if column
         | 
| 238 | 
            -
                   | 
| 239 | 
            -
                   | 
| 219 | 
            +
                  cell_style = (in_header_row = column.table.header_row?) ? nil : column.attributes['style']
         | 
| 220 | 
            +
                  # REVIEW feels hacky to inherit all attributes from column
         | 
| 221 | 
            +
                  update_attributes column.attributes
         | 
| 222 | 
            +
                else
         | 
| 223 | 
            +
                  in_header_row = cell_style = nil
         | 
| 240 224 | 
             
                end
         | 
| 241 225 | 
             
                if attributes
         | 
| 242 | 
            -
                  @colspan = attributes.delete | 
| 243 | 
            -
                  @rowspan = attributes.delete | 
| 244 | 
            -
                  # TODO  | 
| 245 | 
            -
                   | 
| 246 | 
            -
                   | 
| 247 | 
            -
                   | 
| 226 | 
            +
                  @colspan = attributes.delete 'colspan'
         | 
| 227 | 
            +
                  @rowspan = attributes.delete 'rowspan'
         | 
| 228 | 
            +
                  # TODO eventually remove the style attribute from the attributes hash
         | 
| 229 | 
            +
                  #cell_style = attributes.delete 'style' unless in_header_row || !(attributes.key? 'style')
         | 
| 230 | 
            +
                  cell_style = attributes['style'] unless in_header_row || !(attributes.key? 'style')
         | 
| 231 | 
            +
                  if opts[:strip_text]
         | 
| 232 | 
            +
                    if cell_style == :literal || cell_style == :verse
         | 
| 233 | 
            +
                      cell_text = cell_text.rstrip
         | 
| 234 | 
            +
                      cell_text = cell_text.slice 1, cell_text.length - 1 while cell_text.start_with? LF
         | 
| 235 | 
            +
                    else
         | 
| 236 | 
            +
                      cell_text = cell_text.strip
         | 
| 237 | 
            +
                    end
         | 
| 238 | 
            +
                  end
         | 
| 239 | 
            +
                  update_attributes attributes
         | 
| 240 | 
            +
                else
         | 
| 241 | 
            +
                  @colspan = nil
         | 
| 242 | 
            +
                  @rowspan = nil
         | 
| 248 243 | 
             
                end
         | 
| 249 | 
            -
                # only  | 
| 250 | 
            -
                if  | 
| 244 | 
            +
                # NOTE only true for non-header rows
         | 
| 245 | 
            +
                if cell_style == :asciidoc
         | 
| 251 246 | 
             
                  # FIXME hide doctitle from nested document; temporary workaround to fix
         | 
| 252 247 | 
             
                  # nested document seeing doctitle and assuming it has its own document title
         | 
| 253 248 | 
             
                  parent_doctitle = @document.attributes.delete('doctitle')
         | 
| 254 249 | 
             
                  # NOTE we need to process the first line of content as it may not have been processed
         | 
| 255 250 | 
             
                  # the included content cannot expect to match conditional terminators in the remaining
         | 
| 256 251 | 
             
                  # lines of table cell content, it must be self-contained logic
         | 
| 257 | 
            -
                   | 
| 258 | 
            -
                   | 
| 259 | 
            -
             | 
| 260 | 
            -
             | 
| 261 | 
            -
                     | 
| 252 | 
            +
                  # QUESTION should we reset cell_text to nil?
         | 
| 253 | 
            +
                  # QUESTION is is faster to check for :: before splitting?
         | 
| 254 | 
            +
                  inner_document_lines = cell_text.split LF, -1
         | 
| 255 | 
            +
                  if (unprocessed_line1 = inner_document_lines[0]).include? '::'
         | 
| 256 | 
            +
                    preprocessed_lines = (PreprocessorReader.new @document, [unprocessed_line1]).readlines
         | 
| 257 | 
            +
                    unless unprocessed_line1 == preprocessed_lines[0] && preprocessed_lines.size < 2
         | 
| 262 258 | 
             
                      inner_document_lines.shift
         | 
| 263 | 
            -
                      inner_document_lines.unshift(* | 
| 259 | 
            +
                      inner_document_lines.unshift(*preprocessed_lines) unless preprocessed_lines.empty?
         | 
| 264 260 | 
             
                    end
         | 
| 265 | 
            -
                  end
         | 
| 266 | 
            -
                  @inner_document = Document.new(inner_document_lines, :header_footer => false, :parent => @document, :cursor => cursor)
         | 
| 261 | 
            +
                  end unless inner_document_lines.empty?
         | 
| 262 | 
            +
                  @inner_document = Document.new(inner_document_lines, :header_footer => false, :parent => @document, :cursor => opts[:cursor])
         | 
| 267 263 | 
             
                  @document.attributes['doctitle'] = parent_doctitle unless parent_doctitle.nil?
         | 
| 268 264 | 
             
                end
         | 
| 265 | 
            +
                @text = cell_text
         | 
| 266 | 
            +
                @style = cell_style
         | 
| 269 267 | 
             
              end
         | 
| 270 268 |  | 
| 271 | 
            -
              # Public: Get the text  | 
| 269 | 
            +
              # Public: Get the String text of this cell with substitutions applied.
         | 
| 270 | 
            +
              #
         | 
| 271 | 
            +
              # Used for cells in the head row as well as text-only (non-AsciiDoc) cells in
         | 
| 272 | 
            +
              # the foot row and body.
         | 
| 273 | 
            +
              #
         | 
| 274 | 
            +
              # This method shouldn't be used for cells that have the AsciiDoc style.
         | 
| 275 | 
            +
              #
         | 
| 276 | 
            +
              # Returns the converted String text for this Cell
         | 
| 272 277 | 
             
              def text
         | 
| 273 | 
            -
                 | 
| 278 | 
            +
                apply_subs @text, (@style == :literal ? BASIC_SUBS : NORMAL_SUBS)
         | 
| 279 | 
            +
              end
         | 
| 280 | 
            +
             | 
| 281 | 
            +
              # Public: Set the String text.
         | 
| 282 | 
            +
              #
         | 
| 283 | 
            +
              # This method shouldn't be used for cells that have the AsciiDoc style.
         | 
| 284 | 
            +
              #
         | 
| 285 | 
            +
              # Returns the new String text assigned to this Cell
         | 
| 286 | 
            +
              def text= val
         | 
| 287 | 
            +
                @text = val
         | 
| 274 288 | 
             
              end
         | 
| 275 289 |  | 
| 276 290 | 
             
              # Public: Handles the body data (tbody, tfoot), applying styles and partitioning into paragraphs
         | 
| 291 | 
            +
              #
         | 
| 292 | 
            +
              # This method should not be used for cells in the head row or that have the literal or verse style.
         | 
| 293 | 
            +
              #
         | 
| 294 | 
            +
              # Returns the converted String for this Cell
         | 
| 277 295 | 
             
              def content
         | 
| 278 296 | 
             
                if @style == :asciidoc
         | 
| 279 297 | 
             
                  @inner_document.convert
         | 
| @@ -296,11 +314,24 @@ end | |
| 296 314 | 
             
            # instantiated, the row is closed if the cell satisifies the column count and,
         | 
| 297 315 | 
             
            # finally, a new buffer is allocated to track the next cell.
         | 
| 298 316 | 
             
            class Table::ParserContext
         | 
| 317 | 
            +
              # Public: An Array of String keys that represent the table formats in AsciiDoc
         | 
| 318 | 
            +
              #--
         | 
| 319 | 
            +
              # QUESTION should we recognize !sv as a valid format value?
         | 
| 320 | 
            +
              FORMATS = ['psv', 'csv', 'dsv', 'tsv'].to_set
         | 
| 321 | 
            +
             | 
| 322 | 
            +
              # Public: A Hash mapping the AsciiDoc table formats to default delimiters
         | 
| 323 | 
            +
              DELIMITERS = {
         | 
| 324 | 
            +
                'psv' => ['|', /\|/],
         | 
| 325 | 
            +
                'csv' => [',', /,/],
         | 
| 326 | 
            +
                'dsv' => [':', /:/],
         | 
| 327 | 
            +
                'tsv' => [%(\t), /\t/],
         | 
| 328 | 
            +
                '!sv' => ['!', /!/]
         | 
| 329 | 
            +
              }
         | 
| 299 330 |  | 
| 300 331 | 
             
              # Public: The Table currently being parsed
         | 
| 301 332 | 
             
              attr_accessor :table
         | 
| 302 333 |  | 
| 303 | 
            -
              # Public: The AsciiDoc table format (psv, dsv or csv)
         | 
| 334 | 
            +
              # Public: The AsciiDoc table format (psv, dsv, or csv)
         | 
| 304 335 | 
             
              attr_accessor :format
         | 
| 305 336 |  | 
| 306 337 | 
             
              # Public: Get the expected column count for a row
         | 
| @@ -319,25 +350,41 @@ class Table::ParserContext | |
| 319 350 | 
             
              # Public: The cell delimiter compiled Regexp for this table.
         | 
| 320 351 | 
             
              attr_reader :delimiter_re
         | 
| 321 352 |  | 
| 322 | 
            -
              def initialize | 
| 353 | 
            +
              def initialize reader, table, attributes = {}
         | 
| 323 354 | 
             
                @reader = reader
         | 
| 324 355 | 
             
                @table = table
         | 
| 325 | 
            -
                #  | 
| 356 | 
            +
                # IMPORTANT if reader.cursor becomes a reference, this assignment would require .dup
         | 
| 326 357 | 
             
                @last_cursor = reader.cursor
         | 
| 327 | 
            -
             | 
| 328 | 
            -
             | 
| 329 | 
            -
             | 
| 358 | 
            +
             | 
| 359 | 
            +
                if attributes.key? 'format'
         | 
| 360 | 
            +
                  if FORMATS.include?(xsv = attributes['format'])
         | 
| 361 | 
            +
                    if xsv == 'tsv'
         | 
| 362 | 
            +
                      # NOTE tsv is just an alias for csv with a tab separator
         | 
| 363 | 
            +
                      @format = 'csv'
         | 
| 364 | 
            +
                    elsif (@format = xsv) == 'psv' && table.document.nested?
         | 
| 365 | 
            +
                      xsv = '!sv'
         | 
| 366 | 
            +
                    end
         | 
| 367 | 
            +
                  else
         | 
| 368 | 
            +
                    warn %(asciidoctor: ERROR: #{reader.prev_line_info}: illegal table format: #{xsv})
         | 
| 369 | 
            +
                    @format, xsv = 'psv', (table.document.nested? ? '!sv' : 'psv')
         | 
| 330 370 | 
             
                  end
         | 
| 331 371 | 
             
                else
         | 
| 332 | 
            -
                  @format =  | 
| 372 | 
            +
                  @format, xsv = 'psv', (table.document.nested? ? '!sv' : 'psv')
         | 
| 333 373 | 
             
                end
         | 
| 334 374 |  | 
| 335 | 
            -
                 | 
| 336 | 
            -
                  ' | 
| 375 | 
            +
                if attributes.key? 'separator'
         | 
| 376 | 
            +
                  if (sep = attributes['separator']).nil_or_empty?
         | 
| 377 | 
            +
                    @delimiter, @delimiter_re = DELIMITERS[xsv]
         | 
| 378 | 
            +
                  # QUESTION should we support any other escape codes or multiple tabs?
         | 
| 379 | 
            +
                  elsif sep == '\t'
         | 
| 380 | 
            +
                    @delimiter, @delimiter_re = DELIMITERS['tsv']
         | 
| 381 | 
            +
                  else
         | 
| 382 | 
            +
                    @delimiter, @delimiter_re = sep, /#{::Regexp.escape sep}/
         | 
| 383 | 
            +
                  end
         | 
| 337 384 | 
             
                else
         | 
| 338 | 
            -
                   | 
| 385 | 
            +
                  @delimiter, @delimiter_re = DELIMITERS[xsv]
         | 
| 339 386 | 
             
                end
         | 
| 340 | 
            -
             | 
| 387 | 
            +
             | 
| 341 388 | 
             
                @colcount = table.columns.empty? ? -1 : table.columns.size
         | 
| 342 389 | 
             
                @buffer = ''
         | 
| 343 390 | 
             
                @cellspecs = []
         | 
| @@ -364,31 +411,36 @@ class Table::ParserContext | |
| 364 411 | 
             
                @delimiter_re.match(line)
         | 
| 365 412 | 
             
              end
         | 
| 366 413 |  | 
| 367 | 
            -
              # Public: Skip  | 
| 368 | 
            -
              # (either because it was escaped or in a quoted context)
         | 
| 414 | 
            +
              # Public: Skip past the matched delimiter because it's inside quoted text.
         | 
| 369 415 | 
             
              #
         | 
| 370 416 | 
             
              # returns the String after the match
         | 
| 371 | 
            -
              def  | 
| 372 | 
            -
                @buffer = %(#{@buffer}#{ | 
| 417 | 
            +
              def skip_past_delimiter(match)
         | 
| 418 | 
            +
                @buffer = %(#{@buffer}#{match.pre_match}#{@delimiter})
         | 
| 373 419 | 
             
                match.post_match
         | 
| 374 420 | 
             
              end
         | 
| 375 421 |  | 
| 376 | 
            -
              # Public:  | 
| 422 | 
            +
              # Public: Skip past the matched delimiter because it's escaped.
         | 
| 377 423 | 
             
              #
         | 
| 378 | 
            -
              # returns  | 
| 379 | 
            -
               | 
| 380 | 
            -
             | 
| 381 | 
            -
                 | 
| 382 | 
            -
                record.start_with?('"') && !record.start_with?('""') && !record.end_with?('"')
         | 
| 424 | 
            +
              # returns the String after the match
         | 
| 425 | 
            +
              def skip_past_escaped_delimiter(match)
         | 
| 426 | 
            +
                @buffer = %(#{@buffer}#{match.pre_match.chop}#{@delimiter})
         | 
| 427 | 
            +
                match.post_match
         | 
| 383 428 | 
             
              end
         | 
| 384 429 |  | 
| 385 | 
            -
              # Public: Determines whether the buffer  | 
| 430 | 
            +
              # Public: Determines whether the buffer has unclosed quotes. Used for CSV data.
         | 
| 386 431 | 
             
              #
         | 
| 387 | 
            -
              # returns true if the buffer  | 
| 388 | 
            -
              #  | 
| 389 | 
            -
              def  | 
| 390 | 
            -
                 | 
| 391 | 
            -
             | 
| 432 | 
            +
              # returns true if the buffer has unclosed quotes, false if it doesn't or it
         | 
| 433 | 
            +
              # isn't quoted data
         | 
| 434 | 
            +
              def buffer_has_unclosed_quotes? append = nil
         | 
| 435 | 
            +
                if (record = append ? (buffer + append).strip : buffer.strip).start_with? '"'
         | 
| 436 | 
            +
                  if ((trailing_quote = record.end_with? '"') && (record.end_with? '""')) || (record.start_with? '""')
         | 
| 437 | 
            +
                    ((record = record.gsub '""', '').start_with? '"') && !(record.end_with? '"')
         | 
| 438 | 
            +
                  else
         | 
| 439 | 
            +
                    !trailing_quote
         | 
| 440 | 
            +
                  end
         | 
| 441 | 
            +
                else
         | 
| 442 | 
            +
                  false
         | 
| 443 | 
            +
                end
         | 
| 392 444 | 
             
              end
         | 
| 393 445 |  | 
| 394 446 | 
             
              # Public: Takes a cell spec from the stack. Cell specs precede the delimiter, so a
         | 
| @@ -460,11 +512,11 @@ class Table::ParserContext | |
| 460 512 | 
             
              #
         | 
| 461 513 | 
             
              # returns nothing
         | 
| 462 514 | 
             
              def close_cell(eol = false)
         | 
| 463 | 
            -
                cell_text = @buffer.strip
         | 
| 464 | 
            -
                @buffer = ''
         | 
| 465 515 | 
             
                if @format == 'psv'
         | 
| 466 | 
            -
                   | 
| 467 | 
            -
                   | 
| 516 | 
            +
                  strip_text = true
         | 
| 517 | 
            +
                  cell_text = @buffer
         | 
| 518 | 
            +
                  @buffer = ''
         | 
| 519 | 
            +
                  if (cellspec = take_cellspec)
         | 
| 468 520 | 
             
                    repeat = cellspec.delete('repeatcol') || 1
         | 
| 469 521 | 
             
                  else
         | 
| 470 522 | 
             
                    warn %(asciidoctor: ERROR: #{@last_cursor.line_info}: table missing leading separator, recovering automatically)
         | 
| @@ -472,6 +524,9 @@ class Table::ParserContext | |
| 472 524 | 
             
                    repeat = 1
         | 
| 473 525 | 
             
                  end
         | 
| 474 526 | 
             
                else
         | 
| 527 | 
            +
                  strip_text = false
         | 
| 528 | 
            +
                  cell_text = @buffer.strip
         | 
| 529 | 
            +
                  @buffer = ''
         | 
| 475 530 | 
             
                  cellspec = nil
         | 
| 476 531 | 
             
                  repeat = 1
         | 
| 477 532 | 
             
                  if @format == 'csv'
         | 
| @@ -482,8 +537,8 @@ class Table::ParserContext | |
| 482 537 | 
             
                        cell_text = cell_text[1...-1].strip
         | 
| 483 538 | 
             
                      end
         | 
| 484 539 |  | 
| 485 | 
            -
                      #  | 
| 486 | 
            -
                      cell_text = cell_text. | 
| 540 | 
            +
                      # collapse escaped quotes
         | 
| 541 | 
            +
                      cell_text = cell_text.squeeze('"')
         | 
| 487 542 | 
             
                    end
         | 
| 488 543 | 
             
                  end
         | 
| 489 544 | 
             
                end
         | 
| @@ -506,7 +561,7 @@ class Table::ParserContext | |
| 506 561 | 
             
                    end
         | 
| 507 562 | 
             
                  end
         | 
| 508 563 |  | 
| 509 | 
            -
                  cell = Table::Cell.new(column, cell_text, cellspec, @last_cursor)
         | 
| 564 | 
            +
                  cell = Table::Cell.new(column, cell_text, cellspec, :cursor => @last_cursor, :strip_text => strip_text)
         | 
| 510 565 | 
             
                  @last_cursor = @reader.cursor
         | 
| 511 566 | 
             
                  unless !cell.rowspan || cell.rowspan == 1
         | 
| 512 567 | 
             
                    activate_rowspan(cell.rowspan, (cell.colspan || 1))
         | 
    
        data/lib/asciidoctor/version.rb
    CHANGED
    
    
    
        data/man/asciidoctor.1
    CHANGED
    
    | @@ -1,13 +1,13 @@ | |
| 1 1 | 
             
            '\" t
         | 
| 2 2 | 
             
            .\"     Title: asciidoctor
         | 
| 3 3 | 
             
            .\"    Author: Dan Allen, Sarah White, Ryan Waldron
         | 
| 4 | 
            -
            .\" Generator: Asciidoctor 1.5. | 
| 5 | 
            -
            .\"      Date:  | 
| 4 | 
            +
            .\" Generator: Asciidoctor 1.5.6
         | 
| 5 | 
            +
            .\"      Date: 2017-07-12
         | 
| 6 6 | 
             
            .\"    Manual: Asciidoctor Manual
         | 
| 7 | 
            -
            .\"    Source: Asciidoctor 1.5. | 
| 7 | 
            +
            .\"    Source: Asciidoctor 1.5.6
         | 
| 8 8 | 
             
            .\"  Language: English
         | 
| 9 9 | 
             
            .\"
         | 
| 10 | 
            -
            .TH "ASCIIDOCTOR" "1" " | 
| 10 | 
            +
            .TH "ASCIIDOCTOR" "1" "2017-07-12" "Asciidoctor 1.5.6" "Asciidoctor Manual"
         | 
| 11 11 | 
             
            .ie \n(.g .ds Aq \(aq
         | 
| 12 12 | 
             
            .el       .ds Aq '
         | 
| 13 13 | 
             
            .ss \n[.ss] 0
         | 
| @@ -35,7 +35,7 @@ If \fIFILE\fP is \fI\-\fP then the AsciiDoc source is read from standard input. | |
| 35 35 | 
             
            .RS 4
         | 
| 36 36 | 
             
            Base directory containing the document and resources.
         | 
| 37 37 | 
             
            Defaults to the directory containing the source file, or the working directory if the source is read from a stream.
         | 
| 38 | 
            -
             | 
| 38 | 
            +
            When combined with the safe mode setting, can be used to chroot the execution of the program.
         | 
| 39 39 | 
             
            .RE
         | 
| 40 40 | 
             
            .sp
         | 
| 41 41 | 
             
            \fB\-S, \-\-safe\-mode\fP=\fISAFE_MODE\fP
         | 
| @@ -48,7 +48,7 @@ If not set, the safe mode level defaults to \fIunsafe\fP when Asciidoctor is inv | |
| 48 48 | 
             
            \fB\-\-safe\fP
         | 
| 49 49 | 
             
            .RS 4
         | 
| 50 50 | 
             
            Set safe mode level to \fIsafe\fP.
         | 
| 51 | 
            -
            Enables include  | 
| 51 | 
            +
            Enables include directives, but prevents access to ancestor paths of source file.
         | 
| 52 52 | 
             
            Provided for compatibility with the asciidoc command.
         | 
| 53 53 | 
             
            If not set, the safe mode level defaults to \fIunsafe\fP when Asciidoctor is invoked using this script.
         | 
| 54 54 | 
             
            .RE
         | 
| @@ -57,7 +57,7 @@ If not set, the safe mode level defaults to \fIunsafe\fP when Asciidoctor is inv | |
| 57 57 | 
             
            \fB\-a, \-\-attribute\fP=\fIATTRIBUTE\fP
         | 
| 58 58 | 
             
            .RS 4
         | 
| 59 59 | 
             
            Define, override or delete a document attribute.
         | 
| 60 | 
            -
            Command\-line attributes take precedence over attributes defined in the source file.
         | 
| 60 | 
            +
            Command\-line attributes take precedence over attributes defined in the source file unless the value ends with \fI@\fP.
         | 
| 61 61 | 
             
            .sp
         | 
| 62 62 | 
             
            \fIATTRIBUTE\fP is normally formatted as a key\-value pair, in the form \fINAME=VALUE\fP.
         | 
| 63 63 | 
             
            Alternate acceptable forms are \fINAME\fP (where the \fIVALUE\fP defaults to an empty string), \fINAME!\fP (unassigns the \fINAME\fP attribute) and \fINAME=VALUE@\fP (where \fIVALUE\fP does not override value of \fINAME\fP attribute if it\(cqs already defined in the source document).
         | 
| @@ -70,8 +70,8 @@ This option may be specified more than once. | |
| 70 70 | 
             
            .RS 4
         | 
| 71 71 | 
             
            Backend output file format: \fIhtml5\fP, \fIdocbook5\fP, \fIdocbook45\fP and \fImanpage\fP are supported out of the box.
         | 
| 72 72 | 
             
            You can also use the backend alias names \fIhtml\fP (aliased to \fIhtml5\fP) or \fIdocbook\fP (aliased to \fIdocbook5\fP).
         | 
| 73 | 
            +
            Other values can be passed, but if Asciidoctor cannot resolve the backend to a converter, it will fail.
         | 
| 73 74 | 
             
            Defaults to \fIhtml5\fP.
         | 
| 74 | 
            -
            Other options can be passed, but if Asciidoctor cannot find the backend, it will fail during conversion.
         | 
| 75 75 | 
             
            .RE
         | 
| 76 76 | 
             
            .sp
         | 
| 77 77 | 
             
            \fB\-d, \-\-doctype\fP=\fIDOCTYPE\fP
         | 
| @@ -79,7 +79,7 @@ Other options can be passed, but if Asciidoctor cannot find the backend, it will | |
| 79 79 | 
             
            Document type: \fIarticle\fP, \fIbook\fP, \fImanpage\fP or \fIinline\fP.
         | 
| 80 80 | 
             
            Sets the root element when using the \fIdocbook\fP backend and the style class on the HTML body element when using the \fIhtml\fP backend.
         | 
| 81 81 | 
             
            The \fIbook\fP document type allows multiple level\-0 section titles in a single document.
         | 
| 82 | 
            -
            The \fImanpage\fP document type enables parsing of metadata necessary to produce a  | 
| 82 | 
            +
            The \fImanpage\fP document type enables parsing of metadata necessary to produce a man page.
         | 
| 83 83 | 
             
            The \fIinline\fP document type allows the content of a single paragraph to be formatted and returned without wrapping it in a containing element.
         | 
| 84 84 | 
             
            Defaults to \fIarticle\fP.
         | 
| 85 85 | 
             
            .RE
         | 
| @@ -129,9 +129,9 @@ Synonym for \fB\-\-attribute numbered\fP. | |
| 129 129 | 
             
            .RS 4
         | 
| 130 130 | 
             
            Write output to file \fIOUT_FILE\fP.
         | 
| 131 131 | 
             
            Defaults to the base name of the input file suffixed with \fIbackend\fP extension.
         | 
| 132 | 
            -
             | 
| 133 | 
            -
            If  | 
| 134 | 
            -
            If  | 
| 132 | 
            +
            The file is resolved relative to the working directory.
         | 
| 133 | 
            +
            If the input is read from standard input or a named pipe (fifo), then the output file defaults to stdout.
         | 
| 134 | 
            +
            If \fIOUT_FILE\fP is \fI\-\fP, then the output file is written to standard output.
         | 
| 135 135 | 
             
            .RE
         | 
| 136 136 | 
             
            .sp
         | 
| 137 137 | 
             
            \fB\-r, \-\-require\fP=\fILIBRARY\fP
         | 
| @@ -180,9 +180,11 @@ Display timings information (time to read, parse and convert). | |
| 180 180 | 
             
            .RE
         | 
| 181 181 | 
             
            .SS "Program Information"
         | 
| 182 182 | 
             
            .sp
         | 
| 183 | 
            -
            \fB\-h, \-\-help\fP
         | 
| 183 | 
            +
            \fB\-h, \-\-help\fP [\fITOPIC\fP]
         | 
| 184 184 | 
             
            .RS 4
         | 
| 185 | 
            -
             | 
| 185 | 
            +
            Print the help message.
         | 
| 186 | 
            +
            Show the command usage if \fITOPIC\fP is not specified (or not recognized).
         | 
| 187 | 
            +
            Dump the Asciidoctor man page (in troff/groff format) if \fITOPIC\fP is \fImanpage\fP.
         | 
| 186 188 | 
             
            .RE
         | 
| 187 189 | 
             
            .sp
         | 
| 188 190 | 
             
            \fB\-V, \-\-version\fP
         | 
| @@ -193,7 +195,7 @@ Print program version number. | |
| 193 195 | 
             
            .RE
         | 
| 194 196 | 
             
            .SH "ENVIRONMENT"
         | 
| 195 197 | 
             
            .sp
         | 
| 196 | 
            -
            \fBAsciidoctor\fP honors the  | 
| 198 | 
            +
            \fBAsciidoctor\fP honors the \fBSOURCE_DATE_EPOCH\fP environment variable.
         | 
| 197 199 | 
             
            If this variable is assigned an integer value, that value is used as the epoch of all input documents and as the local date and time.
         | 
| 198 200 | 
             
            See \c
         | 
| 199 201 | 
             
            .URL "https://reproducible\-builds.org/specs/source\-date\-epoch/" "" " "
         | 
| @@ -233,7 +235,7 @@ Refer to the \fBAsciidoctor\fP issue tracker at \c | |
| 233 235 | 
             
            .URL "http://discuss.asciidoctor.org" "" ""
         | 
| 234 236 | 
             
            .SH "COPYING"
         | 
| 235 237 | 
             
            .sp
         | 
| 236 | 
            -
            Copyright (C) 2012\- | 
| 238 | 
            +
            Copyright (C) 2012\-2017 Dan Allen, Ryan Waldron and the Asciidoctor Project.
         | 
| 237 239 | 
             
            Free use of this software is granted under the terms of the MIT License.
         | 
| 238 240 | 
             
            .SH "AUTHOR(S)"
         | 
| 239 241 | 
             
            .sp
         |