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
| @@ -102,9 +102,11 @@ module Asciidoctor | |
| 102 102 | 
             
            class PathResolver
         | 
| 103 103 | 
             
              DOT = '.'
         | 
| 104 104 | 
             
              DOT_DOT = '..'
         | 
| 105 | 
            +
              DOT_SLASH = './'
         | 
| 105 106 | 
             
              SLASH = '/'
         | 
| 106 107 | 
             
              BACKSLASH = '\\'
         | 
| 107 | 
            -
               | 
| 108 | 
            +
              DOUBLE_SLASH = '//'
         | 
| 109 | 
            +
              WindowsRootRx = /^[a-zA-Z]:(?:\\|\/)/
         | 
| 108 110 |  | 
| 109 111 | 
             
              attr_accessor :file_separator
         | 
| 110 112 | 
             
              attr_accessor :working_dir
         | 
| @@ -115,16 +117,18 @@ class PathResolver | |
| 115 117 | 
             
              # expanded to an absolute path inside the constructor.
         | 
| 116 118 | 
             
              #
         | 
| 117 119 | 
             
              # file_separator - the String file separator to use for path operations
         | 
| 118 | 
            -
              #                  (optional, default: File:: | 
| 120 | 
            +
              #                  (optional, default: File::SEPARATOR)
         | 
| 119 121 | 
             
              # working_dir    - the String working directory (optional, default: Dir.pwd)
         | 
| 120 122 | 
             
              #
         | 
| 121 | 
            -
              def initialize | 
| 122 | 
            -
                @file_separator = file_separator | 
| 123 | 
            -
                if working_dir | 
| 124 | 
            -
                  @working_dir = File.expand_path | 
| 123 | 
            +
              def initialize file_separator = nil, working_dir = nil
         | 
| 124 | 
            +
                @file_separator = file_separator ? file_separator : (::File::ALT_SEPARATOR || ::File::SEPARATOR)
         | 
| 125 | 
            +
                if working_dir
         | 
| 126 | 
            +
                  @working_dir = (is_root? working_dir) ? working_dir : (::File.expand_path working_dir)
         | 
| 125 127 | 
             
                else
         | 
| 126 | 
            -
                  @working_dir =  | 
| 128 | 
            +
                  @working_dir = ::File.expand_path ::Dir.pwd
         | 
| 127 129 | 
             
                end
         | 
| 130 | 
            +
                @_partition_path_sys = {}
         | 
| 131 | 
            +
                @_partition_path_web = {}
         | 
| 128 132 | 
             
              end
         | 
| 129 133 |  | 
| 130 134 | 
             
              # Public: Check if the specified path is an absolute root path
         | 
| @@ -133,22 +137,33 @@ class PathResolver | |
| 133 137 | 
             
              # path - the String path to check
         | 
| 134 138 | 
             
              #
         | 
| 135 139 | 
             
              # returns a Boolean indicating whether the path is an absolute root path
         | 
| 136 | 
            -
              def is_root? | 
| 137 | 
            -
                 | 
| 140 | 
            +
              def is_root? path
         | 
| 141 | 
            +
                # Unix absolute paths and UNC paths start with slash
         | 
| 142 | 
            +
                if path.start_with? SLASH
         | 
| 138 143 | 
             
                  true
         | 
| 139 | 
            -
                 | 
| 144 | 
            +
                # Windows roots can begin with drive letter
         | 
| 145 | 
            +
                elsif @file_separator == BACKSLASH && WindowsRootRx =~ path
         | 
| 140 146 | 
             
                  true
         | 
| 141 147 | 
             
                else
         | 
| 142 148 | 
             
                  false
         | 
| 143 149 | 
             
                end
         | 
| 144 150 | 
             
              end
         | 
| 145 151 |  | 
| 152 | 
            +
              # Public: Determine if the path is a UNC (root) path
         | 
| 153 | 
            +
              #
         | 
| 154 | 
            +
              # path - the String path to check
         | 
| 155 | 
            +
              #
         | 
| 156 | 
            +
              # returns a Boolean indicating whether the path is a UNC path
         | 
| 157 | 
            +
              def is_unc? path
         | 
| 158 | 
            +
                path.start_with? DOUBLE_SLASH
         | 
| 159 | 
            +
              end
         | 
| 160 | 
            +
             | 
| 146 161 | 
             
              # Public: Determine if the path is an absolute (root) web path
         | 
| 147 162 | 
             
              #
         | 
| 148 163 | 
             
              # path - the String path to check
         | 
| 149 164 | 
             
              #
         | 
| 150 165 | 
             
              # returns a Boolean indicating whether the path is an absolute (root) web path
         | 
| 151 | 
            -
              def is_web_root? | 
| 166 | 
            +
              def is_web_root? path
         | 
| 152 167 | 
             
                path.start_with? SLASH
         | 
| 153 168 | 
             
              end
         | 
| 154 169 |  | 
| @@ -157,9 +172,14 @@ class PathResolver | |
| 157 172 | 
             
              # path - the String path to normalize
         | 
| 158 173 | 
             
              #
         | 
| 159 174 | 
             
              # returns a String path with any backslashes replaced with forward slashes
         | 
| 160 | 
            -
              def posixfy | 
| 161 | 
            -
                 | 
| 162 | 
            -
             | 
| 175 | 
            +
              def posixfy path
         | 
| 176 | 
            +
                if path.nil_or_empty?
         | 
| 177 | 
            +
                  ''
         | 
| 178 | 
            +
                elsif path.include? BACKSLASH
         | 
| 179 | 
            +
                  path.tr BACKSLASH, SLASH
         | 
| 180 | 
            +
                else
         | 
| 181 | 
            +
                  path
         | 
| 182 | 
            +
                end
         | 
| 163 183 | 
             
              end
         | 
| 164 184 |  | 
| 165 185 | 
             
              # Public: Expand the path by resolving any parent references (..)
         | 
| @@ -173,32 +193,76 @@ class PathResolver | |
| 173 193 | 
             
              # path - the String path to expand
         | 
| 174 194 | 
             
              #
         | 
| 175 195 | 
             
              # returns a String path with any parent or self references resolved.
         | 
| 176 | 
            -
              def expand_path | 
| 177 | 
            -
                path_segments, path_root, _ = partition_path | 
| 196 | 
            +
              def expand_path path
         | 
| 197 | 
            +
                path_segments, path_root, _ = partition_path path
         | 
| 178 198 | 
             
                join_path path_segments, path_root
         | 
| 179 199 | 
             
              end
         | 
| 180 200 |  | 
| 181 201 | 
             
              # Public: Partition the path into path segments and remove any empty segments
         | 
| 182 | 
            -
              # or segments that are self references (.). The path is  | 
| 183 | 
            -
              #  | 
| 202 | 
            +
              # or segments that are self references (.). The path is converted to a posix
         | 
| 203 | 
            +
              # path before being partitioned.
         | 
| 184 204 | 
             
              #
         | 
| 185 205 | 
             
              # path     - the String path to partition
         | 
| 186 206 | 
             
              # web_path - a Boolean indicating whether the path should be handled
         | 
| 187 207 | 
             
              #            as a web path (optional, default: false)
         | 
| 188 208 | 
             
              #
         | 
| 189 | 
            -
              #  | 
| 190 | 
            -
              # path root, if the path is absolute | 
| 191 | 
            -
               | 
| 209 | 
            +
              # Returns a 3-item Array containing the Array of String path segments, the
         | 
| 210 | 
            +
              # path root (e.g., '/', './', 'c:/') if the path is absolute and the posix
         | 
| 211 | 
            +
              # version of the path.
         | 
| 212 | 
            +
              #--
         | 
| 213 | 
            +
              # QUESTION is it worth it to normalize slashes? it doubles the time elapsed 
         | 
| 214 | 
            +
              def partition_path path, web_path = false
         | 
| 215 | 
            +
                if (result = web_path ? @_partition_path_web[path] : @_partition_path_sys[path])
         | 
| 216 | 
            +
                  return result
         | 
| 217 | 
            +
                end
         | 
| 218 | 
            +
             | 
| 192 219 | 
             
                posix_path = posixfy path
         | 
| 193 | 
            -
             | 
| 194 | 
            -
                 | 
| 195 | 
            -
             | 
| 196 | 
            -
             | 
| 197 | 
            -
             | 
| 198 | 
            -
             | 
| 199 | 
            -
             | 
| 200 | 
            -
             | 
| 201 | 
            -
             | 
| 220 | 
            +
             | 
| 221 | 
            +
                root = if web_path
         | 
| 222 | 
            +
                  # ex. /sample/path
         | 
| 223 | 
            +
                  if is_web_root? posix_path
         | 
| 224 | 
            +
                    SLASH
         | 
| 225 | 
            +
                  # ex. ./sample/path
         | 
| 226 | 
            +
                  elsif posix_path.start_with? DOT_SLASH
         | 
| 227 | 
            +
                    DOT_SLASH
         | 
| 228 | 
            +
                  # ex. sample/path
         | 
| 229 | 
            +
                  else
         | 
| 230 | 
            +
                    nil
         | 
| 231 | 
            +
                  end
         | 
| 232 | 
            +
                else
         | 
| 233 | 
            +
                  if is_root? posix_path
         | 
| 234 | 
            +
                    # ex. //sample/path
         | 
| 235 | 
            +
                    if is_unc? posix_path
         | 
| 236 | 
            +
                      DOUBLE_SLASH
         | 
| 237 | 
            +
                    # ex. /sample/path
         | 
| 238 | 
            +
                    elsif posix_path.start_with? SLASH
         | 
| 239 | 
            +
                      SLASH
         | 
| 240 | 
            +
                    # ex. c:/sample/path
         | 
| 241 | 
            +
                    else
         | 
| 242 | 
            +
                      posix_path[0..(posix_path.index SLASH)]
         | 
| 243 | 
            +
                    end
         | 
| 244 | 
            +
                  # ex. ./sample/path
         | 
| 245 | 
            +
                  elsif posix_path.start_with? DOT_SLASH
         | 
| 246 | 
            +
                    DOT_SLASH
         | 
| 247 | 
            +
                  # ex. sample/path
         | 
| 248 | 
            +
                  else
         | 
| 249 | 
            +
                    nil
         | 
| 250 | 
            +
                  end
         | 
| 251 | 
            +
                end
         | 
| 252 | 
            +
             | 
| 253 | 
            +
                path_segments = posix_path.split SLASH
         | 
| 254 | 
            +
                # shift twice for a UNC path
         | 
| 255 | 
            +
                if root == DOUBLE_SLASH
         | 
| 256 | 
            +
                  path_segments = path_segments[2..-1]
         | 
| 257 | 
            +
                # shift once for any other root
         | 
| 258 | 
            +
                elsif root
         | 
| 259 | 
            +
                  path_segments.shift
         | 
| 260 | 
            +
                end
         | 
| 261 | 
            +
                # strip out all dot entries
         | 
| 262 | 
            +
                path_segments.delete DOT
         | 
| 263 | 
            +
                # QUESTION should we chomp trailing /? (we pay a small fraction)
         | 
| 264 | 
            +
                #posix_path = posix_path.chomp '/'
         | 
| 265 | 
            +
                (web_path ? @_partition_path_web : @_partition_path_sys)[path] = [path_segments, root, posix_path]
         | 
| 202 266 | 
             
              end
         | 
| 203 267 |  | 
| 204 268 | 
             
              # Public: Join the segments using the posix file separator (since Ruby knows
         | 
| @@ -211,9 +275,9 @@ class PathResolver | |
| 211 275 | 
             
              #
         | 
| 212 276 | 
             
              # returns a String path formed by joining the segments using the posix file
         | 
| 213 277 | 
             
              # separator and prepending the root, if specified
         | 
| 214 | 
            -
              def join_path | 
| 278 | 
            +
              def join_path segments, root = nil
         | 
| 215 279 | 
             
                if root
         | 
| 216 | 
            -
                   | 
| 280 | 
            +
                  %(#{root}#{segments * SLASH})
         | 
| 217 281 | 
             
                else
         | 
| 218 282 | 
             
                  segments * SLASH
         | 
| 219 283 | 
             
                end
         | 
| @@ -237,68 +301,68 @@ class PathResolver | |
| 237 301 | 
             
              # returns a String path that joins the target path with the start path with
         | 
| 238 302 | 
             
              # any parent references resolved and self references removed and enforces
         | 
| 239 303 | 
             
              # that the resolved path be contained within the jail, if provided
         | 
| 240 | 
            -
              def system_path | 
| 241 | 
            -
                recover = opts.fetch | 
| 242 | 
            -
                 | 
| 304 | 
            +
              def system_path target, start, jail = nil, opts = {}
         | 
| 305 | 
            +
                recover = opts.fetch :recover, true
         | 
| 306 | 
            +
                if jail
         | 
| 243 307 | 
             
                  unless is_root? jail
         | 
| 244 | 
            -
                    raise SecurityError,  | 
| 308 | 
            +
                    raise ::SecurityError, %(Jail is not an absolute path: #{jail})
         | 
| 245 309 | 
             
                  end
         | 
| 246 310 | 
             
                  jail = posixfy jail
         | 
| 247 311 | 
             
                end
         | 
| 248 312 |  | 
| 249 | 
            -
                if target. | 
| 313 | 
            +
                if target.nil_or_empty?
         | 
| 250 314 | 
             
                  target_segments = []
         | 
| 251 315 | 
             
                else
         | 
| 252 | 
            -
                  target_segments, target_root, _ = partition_path | 
| 316 | 
            +
                  target_segments, target_root, _ = partition_path target 
         | 
| 253 317 | 
             
                end
         | 
| 254 318 |  | 
| 255 319 | 
             
                if target_segments.empty?
         | 
| 256 | 
            -
                  if start. | 
| 257 | 
            -
                    return jail | 
| 320 | 
            +
                  if start.nil_or_empty?
         | 
| 321 | 
            +
                    return jail ? jail : @working_dir
         | 
| 258 322 | 
             
                  elsif is_root? start
         | 
| 259 | 
            -
                     | 
| 323 | 
            +
                    unless jail
         | 
| 260 324 | 
             
                      return expand_path start
         | 
| 261 325 | 
             
                    end
         | 
| 262 326 | 
             
                  else
         | 
| 263 | 
            -
                    return system_path | 
| 327 | 
            +
                    return system_path start, jail, jail 
         | 
| 264 328 | 
             
                  end
         | 
| 265 329 | 
             
                end
         | 
| 266 330 |  | 
| 267 | 
            -
                if target_root && target_root !=  | 
| 331 | 
            +
                if target_root && target_root != DOT_SLASH
         | 
| 268 332 | 
             
                  resolved_target = join_path target_segments, target_root
         | 
| 269 333 | 
             
                  # if target is absolute and a sub-directory of jail, or
         | 
| 270 334 | 
             
                  # a jail is not in place, let it slide
         | 
| 271 | 
            -
                  if jail | 
| 335 | 
            +
                  if !jail || (resolved_target.start_with? jail)
         | 
| 272 336 | 
             
                    return resolved_target
         | 
| 273 337 | 
             
                  end
         | 
| 274 338 | 
             
                end
         | 
| 275 339 |  | 
| 276 | 
            -
                if start. | 
| 277 | 
            -
                  start = jail | 
| 340 | 
            +
                if start.nil_or_empty?
         | 
| 341 | 
            +
                  start = jail ? jail : @working_dir
         | 
| 278 342 | 
             
                elsif is_root? start
         | 
| 279 343 | 
             
                  start = posixfy start
         | 
| 280 344 | 
             
                else
         | 
| 281 | 
            -
                  start = system_path | 
| 345 | 
            +
                  start = system_path start, jail, jail
         | 
| 282 346 | 
             
                end
         | 
| 283 347 |  | 
| 284 348 | 
             
                # both jail and start have been posixfied at this point
         | 
| 285 349 | 
             
                if jail == start
         | 
| 286 | 
            -
                  jail_segments, jail_root, _ = partition_path | 
| 350 | 
            +
                  jail_segments, jail_root, _ = partition_path jail
         | 
| 287 351 | 
             
                  start_segments = jail_segments.dup
         | 
| 288 | 
            -
                elsif  | 
| 289 | 
            -
                   | 
| 290 | 
            -
                    raise SecurityError,  | 
| 352 | 
            +
                elsif jail
         | 
| 353 | 
            +
                  unless start.start_with? jail
         | 
| 354 | 
            +
                    raise ::SecurityError, %(#{opts[:target_name] || 'Start path'} #{start} is outside of jail: #{jail} (disallowed in safe mode))
         | 
| 291 355 | 
             
                  end
         | 
| 292 356 |  | 
| 293 | 
            -
                  start_segments, start_root, _ = partition_path | 
| 294 | 
            -
                  jail_segments, jail_root, _ = partition_path | 
| 357 | 
            +
                  start_segments, start_root, _ = partition_path start 
         | 
| 358 | 
            +
                  jail_segments, jail_root, _ = partition_path jail
         | 
| 295 359 |  | 
| 296 360 | 
             
                  # Already checked for this condition
         | 
| 297 361 | 
             
                  #if start_root != jail_root
         | 
| 298 | 
            -
                  #  raise SecurityError,  | 
| 362 | 
            +
                  #  raise ::SecurityError, %(Jail root #{jail_root} does not match root of #{opts[:target_name] || 'start path'}: #{start_root})
         | 
| 299 363 | 
             
                  #end
         | 
| 300 364 | 
             
                else
         | 
| 301 | 
            -
                  start_segments, start_root, _ = partition_path | 
| 365 | 
            +
                  start_segments, start_root, _ = partition_path start
         | 
| 302 366 | 
             
                  jail_root = start_root
         | 
| 303 367 | 
             
                end
         | 
| 304 368 |  | 
| @@ -306,13 +370,13 @@ class PathResolver | |
| 306 370 | 
             
                warned = false
         | 
| 307 371 | 
             
                target_segments.each do |segment|
         | 
| 308 372 | 
             
                  if segment == DOT_DOT
         | 
| 309 | 
            -
                    if  | 
| 373 | 
            +
                    if jail
         | 
| 310 374 | 
             
                      if resolved_segments.length > jail_segments.length
         | 
| 311 375 | 
             
                        resolved_segments.pop
         | 
| 312 376 | 
             
                      elsif !recover
         | 
| 313 | 
            -
                        raise SecurityError,  | 
| 377 | 
            +
                        raise ::SecurityError, %(#{opts[:target_name] || 'path'} #{target} refers to location outside jail: #{jail} (disallowed in safe mode))
         | 
| 314 378 | 
             
                      elsif !warned
         | 
| 315 | 
            -
                        warn  | 
| 379 | 
            +
                        warn %(asciidoctor: WARNING: #{opts[:target_name] || 'path'} has illegal reference to ancestor of jail, auto-recovering)
         | 
| 316 380 | 
             
                        warned = true
         | 
| 317 381 | 
             
                      end
         | 
| 318 382 | 
             
                    else
         | 
| @@ -336,39 +400,39 @@ class PathResolver | |
| 336 400 | 
             
              # returns a String path that joins the target path with the
         | 
| 337 401 | 
             
              # start path with any parent references resolved and self
         | 
| 338 402 | 
             
              # references removed
         | 
| 339 | 
            -
              def web_path | 
| 340 | 
            -
                target = posixfy | 
| 341 | 
            -
                start = posixfy | 
| 403 | 
            +
              def web_path target, start = nil
         | 
| 404 | 
            +
                target = posixfy target
         | 
| 405 | 
            +
                start = posixfy start
         | 
| 342 406 | 
             
                uri_prefix = nil
         | 
| 343 407 |  | 
| 344 | 
            -
                unless  | 
| 345 | 
            -
                  target =  | 
| 346 | 
            -
                  if target.include? | 
| 408 | 
            +
                unless start.nil_or_empty? || (is_web_root? target)
         | 
| 409 | 
            +
                  target = %(#{start}#{SLASH}#{target})
         | 
| 410 | 
            +
                  if (target.include? ':') && UriSniffRx =~ target
         | 
| 347 411 | 
             
                    uri_prefix = $~[0]
         | 
| 348 412 | 
             
                    target = target[uri_prefix.length..-1]
         | 
| 349 413 | 
             
                  end
         | 
| 350 414 | 
             
                end
         | 
| 351 415 |  | 
| 352 | 
            -
                target_segments, target_root, _ = partition_path | 
| 353 | 
            -
                resolved_segments =  | 
| 416 | 
            +
                target_segments, target_root, _ = partition_path target, true
         | 
| 417 | 
            +
                resolved_segments = []
         | 
| 418 | 
            +
                target_segments.each do |segment|
         | 
| 354 419 | 
             
                  if segment == DOT_DOT
         | 
| 355 | 
            -
                    if  | 
| 356 | 
            -
                       | 
| 357 | 
            -
                    elsif  | 
| 358 | 
            -
                       | 
| 420 | 
            +
                    if resolved_segments.empty?
         | 
| 421 | 
            +
                      resolved_segments << segment unless target_root && target_root != DOT_SLASH
         | 
| 422 | 
            +
                    elsif resolved_segments[-1] == DOT_DOT
         | 
| 423 | 
            +
                      resolved_segments << segment
         | 
| 359 424 | 
             
                    else
         | 
| 360 | 
            -
                       | 
| 425 | 
            +
                      resolved_segments.pop
         | 
| 361 426 | 
             
                    end
         | 
| 362 427 | 
             
                  else
         | 
| 363 | 
            -
                     | 
| 428 | 
            +
                    resolved_segments << segment
         | 
| 364 429 | 
             
                  end
         | 
| 365 | 
            -
                  accum
         | 
| 366 430 | 
             
                end
         | 
| 367 431 |  | 
| 368 | 
            -
                if uri_prefix | 
| 369 | 
            -
                  join_path resolved_segments, target_root
         | 
| 432 | 
            +
                if uri_prefix
         | 
| 433 | 
            +
                  %(#{uri_prefix}#{join_path resolved_segments, target_root})
         | 
| 370 434 | 
             
                else
         | 
| 371 | 
            -
                   | 
| 435 | 
            +
                  join_path resolved_segments, target_root
         | 
| 372 436 | 
             
                end
         | 
| 373 437 | 
             
              end
         | 
| 374 438 |  | 
| @@ -380,7 +444,7 @@ class PathResolver | |
| 380 444 | 
             
              # base_directory - An absolute base directory as a String
         | 
| 381 445 | 
             
              #
         | 
| 382 446 | 
             
              # Return the relative path String of the filename calculated from the base directory
         | 
| 383 | 
            -
              def relative_path | 
| 447 | 
            +
              def relative_path filename, base_directory
         | 
| 384 448 | 
             
                if (is_root? filename) && (is_root? base_directory)
         | 
| 385 449 | 
             
                  offset = base_directory.chomp(@file_separator).length + 1
         | 
| 386 450 | 
             
                  filename[offset..-1]
         | 
    
        data/lib/asciidoctor/reader.rb
    CHANGED
    
    | @@ -17,6 +17,8 @@ class Reader | |
| 17 17 | 
             
                def line_info
         | 
| 18 18 | 
             
                  %(#{path}: line #{lineno})
         | 
| 19 19 | 
             
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                alias :to_s :line_info
         | 
| 20 22 | 
             
              end
         | 
| 21 23 |  | 
| 22 24 | 
             
              attr_reader :file
         | 
| @@ -33,34 +35,33 @@ class Reader | |
| 33 35 | 
             
              attr_accessor :process_lines
         | 
| 34 36 |  | 
| 35 37 | 
             
              # Public: Initialize the Reader object
         | 
| 36 | 
            -
              def initialize data = nil, cursor = nil
         | 
| 37 | 
            -
                if cursor | 
| 38 | 
            +
              def initialize data = nil, cursor = nil, opts = {:normalize => false}
         | 
| 39 | 
            +
                if !cursor
         | 
| 38 40 | 
             
                  @file = @dir = nil
         | 
| 39 41 | 
             
                  @path = '<stdin>'
         | 
| 40 42 | 
             
                  @lineno = 1 # IMPORTANT lineno assignment must proceed prepare_lines call!
         | 
| 41 | 
            -
                elsif cursor.is_a? String
         | 
| 43 | 
            +
                elsif cursor.is_a? ::String
         | 
| 42 44 | 
             
                  @file = cursor
         | 
| 43 | 
            -
                  @dir = File. | 
| 44 | 
            -
                  @path = File.basename @file
         | 
| 45 | 
            +
                  @dir, @path = ::File.split @file
         | 
| 45 46 | 
             
                  @lineno = 1 # IMPORTANT lineno assignment must proceed prepare_lines call!
         | 
| 46 47 | 
             
                else
         | 
| 47 48 | 
             
                  @file = cursor.file
         | 
| 48 49 | 
             
                  @dir = cursor.dir
         | 
| 49 50 | 
             
                  @path = cursor.path || '<stdin>'
         | 
| 50 | 
            -
                   | 
| 51 | 
            -
                     | 
| 51 | 
            +
                  if @file
         | 
| 52 | 
            +
                    unless @dir
         | 
| 52 53 | 
             
                      # REVIEW might to look at this assignment closer
         | 
| 53 | 
            -
                      @dir = File.dirname @file
         | 
| 54 | 
            +
                      @dir = ::File.dirname @file
         | 
| 54 55 | 
             
                      @dir = nil if @dir == '.' # right?
         | 
| 55 56 | 
             
                    end
         | 
| 56 57 |  | 
| 57 | 
            -
                     | 
| 58 | 
            -
                      @path = File.basename @file
         | 
| 58 | 
            +
                    unless cursor.path
         | 
| 59 | 
            +
                      @path = ::File.basename @file
         | 
| 59 60 | 
             
                    end
         | 
| 60 61 | 
             
                  end
         | 
| 61 62 | 
             
                  @lineno = cursor.lineno || 1 # IMPORTANT lineno assignment must proceed prepare_lines call!
         | 
| 62 63 | 
             
                end
         | 
| 63 | 
            -
                @lines = data | 
| 64 | 
            +
                @lines = data ? (prepare_lines data, opts) : []
         | 
| 64 65 | 
             
                @source_lines = @lines.dup
         | 
| 65 66 | 
             
                @eof = @lines.empty?
         | 
| 66 67 | 
             
                @look_ahead = 0
         | 
| @@ -77,14 +78,24 @@ class Reader | |
| 77 78 | 
             
              #
         | 
| 78 79 | 
             
              # Any leading or trailing blank lines are also removed.
         | 
| 79 80 | 
             
              #
         | 
| 80 | 
            -
              # The normalized lines are assigned to the @lines instance variable.
         | 
| 81 | 
            -
              #
         | 
| 82 81 | 
             
              # data - A String Array of input data to be normalized
         | 
| 83 82 | 
             
              # opts - A Hash of options to control what cleansing is done
         | 
| 84 83 | 
             
              #
         | 
| 85 84 | 
             
              # Returns The String lines extracted from the data
         | 
| 86 85 | 
             
              def prepare_lines data, opts = {}
         | 
| 87 | 
            -
                data.is_a? | 
| 86 | 
            +
                if data.is_a? ::String
         | 
| 87 | 
            +
                  if opts[:normalize]
         | 
| 88 | 
            +
                    Helpers.normalize_lines_from_string data
         | 
| 89 | 
            +
                  else
         | 
| 90 | 
            +
                    data.split EOL
         | 
| 91 | 
            +
                  end
         | 
| 92 | 
            +
                else
         | 
| 93 | 
            +
                  if opts[:normalize]
         | 
| 94 | 
            +
                    Helpers.normalize_lines_array data
         | 
| 95 | 
            +
                  else
         | 
| 96 | 
            +
                    data.dup
         | 
| 97 | 
            +
                  end
         | 
| 98 | 
            +
                end
         | 
| 88 99 | 
             
              end
         | 
| 89 100 |  | 
| 90 101 | 
             
              # Internal: Processes a previously unvisited line
         | 
| @@ -118,7 +129,7 @@ class Reader | |
| 118 129 | 
             
              #
         | 
| 119 130 | 
             
              # Returns True if the there are no more lines or if the next line is empty
         | 
| 120 131 | 
             
              def next_line_empty?
         | 
| 121 | 
            -
                 | 
| 132 | 
            +
                peek_line.nil_or_empty?
         | 
| 122 133 | 
             
              end
         | 
| 123 134 |  | 
| 124 135 | 
             
              # Public: Peek at the next line of source data. Processes the line, if not
         | 
| @@ -126,7 +137,7 @@ class Reader | |
| 126 137 | 
             
              #
         | 
| 127 138 | 
             
              # This method will probe the reader for more lines. If there is a next line
         | 
| 128 139 | 
             
              # that has not previously been visited, the line is passed to the
         | 
| 129 | 
            -
              # Reader# | 
| 140 | 
            +
              # Reader#process_line method to be initialized. This call gives
         | 
| 130 141 | 
             
              # sub-classess the opportunity to do preprocessing. If the return value of
         | 
| 131 142 | 
             
              # the Reader#process_line is nil, the data is assumed to be changed and
         | 
| 132 143 | 
             
              # Reader#peek_line is invoked again to perform further processing.
         | 
| @@ -138,7 +149,7 @@ class Reader | |
| 138 149 | 
             
              # Returns nil if there is no more data.
         | 
| 139 150 | 
             
              def peek_line direct = false
         | 
| 140 151 | 
             
                if direct || @look_ahead > 0
         | 
| 141 | 
            -
                  @unescape_next_line ? @lines | 
| 152 | 
            +
                  @unescape_next_line ? @lines[0][1..-1] : @lines[0]
         | 
| 142 153 | 
             
                elsif @eof || @lines.empty?
         | 
| 143 154 | 
             
                  @eof = true
         | 
| 144 155 | 
             
                  @look_ahead = 0
         | 
| @@ -147,7 +158,7 @@ class Reader | |
| 147 158 | 
             
                  # FIXME the problem with this approach is that we aren't
         | 
| 148 159 | 
             
                  # retaining the modified line (hence the @unescape_next_line tweak)
         | 
| 149 160 | 
             
                  # perhaps we need a stack of proxy lines
         | 
| 150 | 
            -
                  if (line = process_line @lines | 
| 161 | 
            +
                  if !(line = process_line @lines[0])
         | 
| 151 162 | 
             
                    peek_line
         | 
| 152 163 | 
             
                  else
         | 
| 153 164 | 
             
                    line
         | 
| @@ -171,7 +182,7 @@ class Reader | |
| 171 182 | 
             
              def peek_lines num = 1, direct = true
         | 
| 172 183 | 
             
                old_look_ahead = @look_ahead
         | 
| 173 184 | 
             
                result = []
         | 
| 174 | 
            -
                 | 
| 185 | 
            +
                num.times do
         | 
| 175 186 | 
             
                  if (line = read_line direct)
         | 
| 176 187 | 
             
                    result << line
         | 
| 177 188 | 
             
                  else
         | 
| @@ -213,7 +224,7 @@ class Reader | |
| 213 224 | 
             
              def read_lines
         | 
| 214 225 | 
             
                lines = []
         | 
| 215 226 | 
             
                while has_more_lines?
         | 
| 216 | 
            -
                  lines <<  | 
| 227 | 
            +
                  lines << shift
         | 
| 217 228 | 
             
                end
         | 
| 218 229 | 
             
                lines
         | 
| 219 230 | 
             
              end
         | 
| @@ -225,7 +236,7 @@ class Reader | |
| 225 236 | 
             
              #
         | 
| 226 237 | 
             
              # Returns the lines read joined as a String
         | 
| 227 238 | 
             
              def read
         | 
| 228 | 
            -
                read_lines | 
| 239 | 
            +
                read_lines * EOL
         | 
| 229 240 | 
             
              end
         | 
| 230 241 |  | 
| 231 242 | 
             
              # Public: Advance to the next line by discarding the line at the front of the stack
         | 
| @@ -235,7 +246,7 @@ class Reader | |
| 235 246 | 
             
              #
         | 
| 236 247 | 
             
              # returns a Boolean indicating whether there was a line to discard.
         | 
| 237 248 | 
             
              def advance direct = true
         | 
| 238 | 
            -
                 | 
| 249 | 
            +
                !!read_line(direct)
         | 
| 239 250 | 
             
              end
         | 
| 240 251 |  | 
| 241 252 | 
             
              # Public: Push the String line onto the beginning of the Array of source data.
         | 
| @@ -283,13 +294,13 @@ class Reader | |
| 283 294 | 
             
              # Examples
         | 
| 284 295 | 
             
              #
         | 
| 285 296 | 
             
              #   @lines
         | 
| 286 | 
            -
              #   => [" | 
| 297 | 
            +
              #   => ["", "", "Foo", "Bar", ""]
         | 
| 287 298 | 
             
              #
         | 
| 288 299 | 
             
              #   skip_blank_lines
         | 
| 289 300 | 
             
              #   => 2
         | 
| 290 301 | 
             
              #
         | 
| 291 302 | 
             
              #   @lines
         | 
| 292 | 
            -
              #   => ["Foo | 
| 303 | 
            +
              #   => ["Foo", "Bar", ""]
         | 
| 293 304 | 
             
              #
         | 
| 294 305 | 
             
              # Returns an Integer of the number of lines skipped
         | 
| 295 306 | 
             
              def skip_blank_lines
         | 
| @@ -298,7 +309,7 @@ class Reader | |
| 298 309 | 
             
                num_skipped = 0
         | 
| 299 310 | 
             
                # optimized code for shortest execution path
         | 
| 300 311 | 
             
                while (next_line = peek_line)
         | 
| 301 | 
            -
                  if next_line. | 
| 312 | 
            +
                  if next_line.empty?
         | 
| 302 313 | 
             
                    advance
         | 
| 303 314 | 
             
                    num_skipped += 1
         | 
| 304 315 | 
             
                  else
         | 
| @@ -313,13 +324,13 @@ class Reader | |
| 313 324 | 
             
              #
         | 
| 314 325 | 
             
              # Examples
         | 
| 315 326 | 
             
              #   @lines
         | 
| 316 | 
            -
              #   => ["// foo | 
| 327 | 
            +
              #   => ["// foo", "bar"]
         | 
| 317 328 | 
             
              #
         | 
| 318 329 | 
             
              #   comment_lines = skip_comment_lines
         | 
| 319 | 
            -
              #   => ["// foo | 
| 330 | 
            +
              #   => ["// foo"]
         | 
| 320 331 | 
             
              #
         | 
| 321 332 | 
             
              #   @lines
         | 
| 322 | 
            -
              #   => ["bar | 
| 333 | 
            +
              #   => ["bar"]
         | 
| 323 334 | 
             
              #
         | 
| 324 335 | 
             
              # Returns the Array of lines that were skipped
         | 
| 325 336 | 
             
              def skip_comment_lines opts = {}
         | 
| @@ -328,13 +339,13 @@ class Reader | |
| 328 339 | 
             
                comment_lines = []
         | 
| 329 340 | 
             
                include_blank_lines = opts[:include_blank_lines]
         | 
| 330 341 | 
             
                while (next_line = peek_line)
         | 
| 331 | 
            -
                  if include_blank_lines && next_line. | 
| 332 | 
            -
                    comment_lines <<  | 
| 333 | 
            -
                  elsif (commentish = next_line.start_with?('//')) && (match =  | 
| 334 | 
            -
                    comment_lines <<  | 
| 342 | 
            +
                  if include_blank_lines && next_line.empty?
         | 
| 343 | 
            +
                    comment_lines << shift
         | 
| 344 | 
            +
                  elsif (commentish = next_line.start_with?('//')) && (match = CommentBlockRx.match(next_line))
         | 
| 345 | 
            +
                    comment_lines << shift
         | 
| 335 346 | 
             
                    comment_lines.push(*(read_lines_until(:terminator => match[0], :read_last_line => true, :skip_processing => true)))
         | 
| 336 | 
            -
                  elsif commentish && next_line | 
| 337 | 
            -
                    comment_lines <<  | 
| 347 | 
            +
                  elsif commentish && CommentLineRx =~ next_line
         | 
| 348 | 
            +
                    comment_lines << shift
         | 
| 338 349 | 
             
                  else
         | 
| 339 350 | 
             
                    break
         | 
| 340 351 | 
             
                  end
         | 
| @@ -350,8 +361,8 @@ class Reader | |
| 350 361 | 
             
                comment_lines = []
         | 
| 351 362 | 
             
                # optimized code for shortest execution path
         | 
| 352 363 | 
             
                while (next_line = peek_line)
         | 
| 353 | 
            -
                  if next_line | 
| 354 | 
            -
                    comment_lines <<  | 
| 364 | 
            +
                  if CommentLineRx =~ next_line
         | 
| 365 | 
            +
                    comment_lines << shift
         | 
| 355 366 | 
             
                  else
         | 
| 356 367 | 
             
                    break
         | 
| 357 368 | 
             
                  end
         | 
| @@ -399,12 +410,16 @@ class Reader | |
| 399 410 | 
             
              #
         | 
| 400 411 | 
             
              # Examples
         | 
| 401 412 | 
             
              #
         | 
| 402 | 
            -
              #    | 
| 403 | 
            -
              # | 
| 404 | 
            -
              # | 
| 413 | 
            +
              #   data = [
         | 
| 414 | 
            +
              #     "First line\n",
         | 
| 415 | 
            +
              #     "Second line\n",
         | 
| 416 | 
            +
              #     "\n",
         | 
| 417 | 
            +
              #     "Third line\n",
         | 
| 418 | 
            +
              #   ]
         | 
| 419 | 
            +
              #   reader = Reader.new data, nil, :normalize => true
         | 
| 405 420 | 
             
              #
         | 
| 406 421 | 
             
              #   reader.read_lines_until
         | 
| 407 | 
            -
              #   => ["First  | 
| 422 | 
            +
              #   => ["First line", "Second line"]
         | 
| 408 423 | 
             
              def read_lines_until options = {}
         | 
| 409 424 | 
             
                result = []
         | 
| 410 425 | 
             
                advance if options[:skip_first_line]
         | 
| @@ -415,34 +430,32 @@ class Reader | |
| 415 430 | 
             
                  restore_process_lines = false
         | 
| 416 431 | 
             
                end
         | 
| 417 432 |  | 
| 418 | 
            -
                has_block = block_given?
         | 
| 419 433 | 
             
                if (terminator = options[:terminator])
         | 
| 420 434 | 
             
                  break_on_blank_lines = false
         | 
| 421 435 | 
             
                  break_on_list_continuation = false
         | 
| 422 | 
            -
                  chomp_last_line = options.fetch :chomp_last_line, false
         | 
| 423 436 | 
             
                else
         | 
| 424 437 | 
             
                  break_on_blank_lines = options[:break_on_blank_lines]
         | 
| 425 438 | 
             
                  break_on_list_continuation = options[:break_on_list_continuation]
         | 
| 426 | 
            -
                  chomp_last_line = break_on_blank_lines
         | 
| 427 439 | 
             
                end
         | 
| 428 440 | 
             
                skip_line_comments = options[:skip_line_comments]
         | 
| 429 441 | 
             
                line_read = false
         | 
| 430 442 | 
             
                line_restored = false
         | 
| 431 443 |  | 
| 432 | 
            -
                 | 
| 433 | 
            -
             | 
| 434 | 
            -
             | 
| 435 | 
            -
                     | 
| 436 | 
            -
                     | 
| 437 | 
            -
                     | 
| 444 | 
            +
                complete = false
         | 
| 445 | 
            +
                while !complete && (line = read_line)
         | 
| 446 | 
            +
                  complete = while true
         | 
| 447 | 
            +
                    break true if terminator && line == terminator
         | 
| 448 | 
            +
                    # QUESTION: can we get away with line.empty? here?
         | 
| 449 | 
            +
                    break true if break_on_blank_lines && line.empty?
         | 
| 450 | 
            +
                    if break_on_list_continuation && line_read && line == LIST_CONTINUATION
         | 
| 438 451 | 
             
                      options[:preserve_last_line] = true
         | 
| 439 452 | 
             
                      break true
         | 
| 440 453 | 
             
                    end
         | 
| 441 | 
            -
                    break true if  | 
| 454 | 
            +
                    break true if block_given? && (yield line)
         | 
| 442 455 | 
             
                    break false
         | 
| 443 456 | 
             
                  end
         | 
| 444 457 |  | 
| 445 | 
            -
                  if  | 
| 458 | 
            +
                  if complete
         | 
| 446 459 | 
             
                    if options[:read_last_line]
         | 
| 447 460 | 
             
                      result << line
         | 
| 448 461 | 
             
                      line_read = true
         | 
| @@ -451,27 +464,27 @@ class Reader | |
| 451 464 | 
             
                      restore_line line
         | 
| 452 465 | 
             
                      line_restored = true
         | 
| 453 466 | 
             
                    end
         | 
| 454 | 
            -
             | 
| 455 | 
            -
             | 
| 456 | 
            -
             | 
| 457 | 
            -
             | 
| 458 | 
            -
                     | 
| 459 | 
            -
                    line_read = true
         | 
| 467 | 
            +
                  else
         | 
| 468 | 
            +
                    unless skip_line_comments && line.start_with?('//') && CommentLineRx =~ line
         | 
| 469 | 
            +
                      result << line
         | 
| 470 | 
            +
                      line_read = true
         | 
| 471 | 
            +
                    end
         | 
| 460 472 | 
             
                  end
         | 
| 461 473 | 
             
                end
         | 
| 462 474 |  | 
| 463 | 
            -
                if chomp_last_line && line_read
         | 
| 464 | 
            -
                  result << result.pop.chomp
         | 
| 465 | 
            -
                end
         | 
| 466 | 
            -
             | 
| 467 475 | 
             
                if restore_process_lines
         | 
| 468 476 | 
             
                  @process_lines = true
         | 
| 469 | 
            -
                  @look_ahead -= 1 if line_restored && terminator | 
| 477 | 
            +
                  @look_ahead -= 1 if line_restored && !terminator
         | 
| 470 478 | 
             
                end
         | 
| 471 479 | 
             
                result
         | 
| 472 480 | 
             
              end
         | 
| 473 481 |  | 
| 474 482 | 
             
              # Internal: Shift the line off the stack and increment the lineno
         | 
| 483 | 
            +
              #
         | 
| 484 | 
            +
              # This method can be used directly when you've already called peek_line
         | 
| 485 | 
            +
              # and determined that you do, in fact, want to pluck that line off the stack.
         | 
| 486 | 
            +
              #
         | 
| 487 | 
            +
              # Returns The String line at the top of the stack
         | 
| 475 488 | 
             
              def shift
         | 
| 476 489 | 
             
                @lineno += 1
         | 
| 477 490 | 
             
                @look_ahead -= 1 unless @look_ahead == 0
         | 
| @@ -511,12 +524,12 @@ class Reader | |
| 511 524 |  | 
| 512 525 | 
             
              # Public: Get a copy of the remaining lines managed by this Reader joined as a String
         | 
| 513 526 | 
             
              def string
         | 
| 514 | 
            -
                @lines | 
| 527 | 
            +
                @lines * EOL
         | 
| 515 528 | 
             
              end
         | 
| 516 529 |  | 
| 517 530 | 
             
              # Public: Get the source lines for this Reader joined as a String
         | 
| 518 531 | 
             
              def source
         | 
| 519 | 
            -
                @source_lines | 
| 532 | 
            +
                @source_lines * EOL
         | 
| 520 533 | 
             
              end
         | 
| 521 534 |  | 
| 522 535 | 
             
              # Public: Get a summary of this Reader.
         | 
| @@ -537,7 +550,7 @@ class PreprocessorReader < Reader | |
| 537 550 | 
             
              # Public: Initialize the PreprocessorReader object
         | 
| 538 551 | 
             
              def initialize document, data = nil, cursor = nil
         | 
| 539 552 | 
             
                @document = document
         | 
| 540 | 
            -
                super data, cursor
         | 
| 553 | 
            +
                super data, cursor, :normalize => true
         | 
| 541 554 | 
             
                include_depth_default = document.attributes.fetch('max-include-depth', 64).to_i
         | 
| 542 555 | 
             
                include_depth_default = 0 if include_depth_default < 0
         | 
| 543 556 | 
             
                # track both absolute depth for comparing to size of include stack and relative depth for reporting
         | 
| @@ -546,39 +559,26 @@ class PreprocessorReader < Reader | |
| 546 559 | 
             
                @includes = (document.references[:includes] ||= [])
         | 
| 547 560 | 
             
                @skipping = false
         | 
| 548 561 | 
             
                @conditional_stack = []
         | 
| 549 | 
            -
                @ | 
| 562 | 
            +
                @include_processor_extensions = nil
         | 
| 550 563 | 
             
              end
         | 
| 551 564 |  | 
| 552 565 | 
             
              def prepare_lines data, opts = {}
         | 
| 553 | 
            -
                 | 
| 554 | 
            -
                  if ::Asciidoctor::FORCE_ENCODING
         | 
| 555 | 
            -
                    result = data.each_line.map {|line| "#{line.rstrip.force_encoding ::Encoding::UTF_8}#{::Asciidoctor::EOL}" }
         | 
| 556 | 
            -
                  else
         | 
| 557 | 
            -
                    result = data.each_line.map {|line| "#{line.rstrip}#{::Asciidoctor::EOL}" }
         | 
| 558 | 
            -
                  end
         | 
| 559 | 
            -
                else
         | 
| 560 | 
            -
                  if ::Asciidoctor::FORCE_ENCODING
         | 
| 561 | 
            -
                    result = data.map {|line| "#{line.rstrip.force_encoding ::Encoding::UTF_8}#{::Asciidoctor::EOL}" }
         | 
| 562 | 
            -
                  else
         | 
| 563 | 
            -
                    result = data.map {|line| "#{line.rstrip}#{::Asciidoctor::EOL}" }
         | 
| 564 | 
            -
                  end
         | 
| 565 | 
            -
                end
         | 
| 566 | 
            +
                result = super
         | 
| 566 567 |  | 
| 567 568 | 
             
                # QUESTION should this work for AsciiDoc table cell content? Currently it does not.
         | 
| 568 | 
            -
                 | 
| 569 | 
            +
                if @document && (@document.attributes.has_key? 'skip-front-matter')
         | 
| 569 570 | 
             
                  if (front_matter = skip_front_matter! result)
         | 
| 570 | 
            -
                    @document.attributes['front-matter'] = front_matter | 
| 571 | 
            +
                    @document.attributes['front-matter'] = front_matter * EOL
         | 
| 571 572 | 
             
                  end
         | 
| 572 573 | 
             
                end
         | 
| 573 574 |  | 
| 574 | 
            -
                 | 
| 575 | 
            -
             | 
| 576 | 
            -
                  result. | 
| 577 | 
            -
                  result.pop while !(last = result.last).nil? && last == ::Asciidoctor::EOL
         | 
| 575 | 
            +
                if opts.fetch :condense, true
         | 
| 576 | 
            +
                  result.shift && @lineno += 1 while (first = result[0]) && first.empty?
         | 
| 577 | 
            +
                  result.pop while (last = result[-1]) && last.empty?
         | 
| 578 578 | 
             
                end
         | 
| 579 579 |  | 
| 580 580 | 
             
                if (indent = opts.fetch(:indent, nil))
         | 
| 581 | 
            -
                   | 
| 581 | 
            +
                  Parser.reset_block_indent! result, indent.to_i
         | 
| 582 582 | 
             
                end
         | 
| 583 583 |  | 
| 584 584 | 
             
                result
         | 
| @@ -587,55 +587,63 @@ class PreprocessorReader < Reader | |
| 587 587 | 
             
              def process_line line
         | 
| 588 588 | 
             
                return line unless @process_lines
         | 
| 589 589 |  | 
| 590 | 
            -
                if line. | 
| 590 | 
            +
                if line.empty?
         | 
| 591 591 | 
             
                  @look_ahead += 1
         | 
| 592 592 | 
             
                  return ''
         | 
| 593 593 | 
             
                end
         | 
| 594 594 |  | 
| 595 | 
            -
                 | 
| 596 | 
            -
                if  | 
| 597 | 
            -
                   | 
| 598 | 
            -
             | 
| 599 | 
            -
                     | 
| 600 | 
            -
             | 
| 601 | 
            -
             | 
| 602 | 
            -
             | 
| 603 | 
            -
                    if preprocess_conditional_inclusion(*match.captures)
         | 
| 604 | 
            -
                      # move the pointer past the conditional line
         | 
| 605 | 
            -
                      advance
         | 
| 606 | 
            -
                      # treat next line as uncharted territory
         | 
| 607 | 
            -
                      nil
         | 
| 595 | 
            +
                # NOTE highly optimized
         | 
| 596 | 
            +
                if line.end_with?(']') && !line.start_with?('[') && line.include?('::')
         | 
| 597 | 
            +
                  if line.include?('if') && (match = ConditionalDirectiveRx.match(line))
         | 
| 598 | 
            +
                    # if escaped, mark as processed and return line unescaped
         | 
| 599 | 
            +
                    if line.start_with?('\\')
         | 
| 600 | 
            +
                      @unescape_next_line = true
         | 
| 601 | 
            +
                      @look_ahead += 1
         | 
| 602 | 
            +
                      line[1..-1]
         | 
| 608 603 | 
             
                    else
         | 
| 609 | 
            -
                       | 
| 610 | 
            -
             | 
| 604 | 
            +
                      if preprocess_conditional_inclusion(*match.captures)
         | 
| 605 | 
            +
                        # move the pointer past the conditional line
         | 
| 606 | 
            +
                        advance
         | 
| 607 | 
            +
                        # treat next line as uncharted territory
         | 
| 608 | 
            +
                        nil
         | 
| 609 | 
            +
                      else
         | 
| 610 | 
            +
                        # the line was not a valid conditional line
         | 
| 611 | 
            +
                        # mark it as visited and return it
         | 
| 612 | 
            +
                        @look_ahead += 1
         | 
| 613 | 
            +
                        line
         | 
| 614 | 
            +
                      end
         | 
| 615 | 
            +
                    end
         | 
| 616 | 
            +
                  elsif @skipping
         | 
| 617 | 
            +
                    advance
         | 
| 618 | 
            +
                    nil
         | 
| 619 | 
            +
                  elsif ((escaped = line.start_with?('\\include::')) || line.start_with?('include::')) && (match = IncludeDirectiveRx.match(line))
         | 
| 620 | 
            +
                    # if escaped, mark as processed and return line unescaped
         | 
| 621 | 
            +
                    if escaped
         | 
| 622 | 
            +
                      @unescape_next_line = true
         | 
| 611 623 | 
             
                      @look_ahead += 1
         | 
| 612 | 
            -
                      line
         | 
| 624 | 
            +
                      line[1..-1]
         | 
| 625 | 
            +
                    else
         | 
| 626 | 
            +
                      # QUESTION should we strip whitespace from raw attributes in Substitutors#parse_attributes? (check perf)
         | 
| 627 | 
            +
                      if preprocess_include match[1], match[2].strip
         | 
| 628 | 
            +
                        # peek again since the content has changed
         | 
| 629 | 
            +
                        nil
         | 
| 630 | 
            +
                      else
         | 
| 631 | 
            +
                        # the line was not a valid include line and is unchanged
         | 
| 632 | 
            +
                        # mark it as visited and return it
         | 
| 633 | 
            +
                        @look_ahead += 1
         | 
| 634 | 
            +
                        line
         | 
| 635 | 
            +
                      end
         | 
| 613 636 | 
             
                    end
         | 
| 637 | 
            +
                  else
         | 
| 638 | 
            +
                    # NOTE optimization to inline super
         | 
| 639 | 
            +
                    @look_ahead += 1
         | 
| 640 | 
            +
                    line
         | 
| 614 641 | 
             
                  end
         | 
| 615 642 | 
             
                elsif @skipping
         | 
| 616 643 | 
             
                  advance
         | 
| 617 | 
            -
                  nil | 
| 618 | 
            -
                elsif macroish && line.include?('include::') && (match = line.match(REGEXP[:include_macro]))
         | 
| 619 | 
            -
                  # if escaped, mark as processed and return line unescaped
         | 
| 620 | 
            -
                  if line.start_with? '\\'
         | 
| 621 | 
            -
                    @unescape_next_line = true
         | 
| 622 | 
            -
                    @look_ahead += 1
         | 
| 623 | 
            -
                    line[1..-1]
         | 
| 624 | 
            -
                  else
         | 
| 625 | 
            -
                    # QUESTION should we strip whitespace from raw attributes in Substituters#parse_attributes? (check perf)
         | 
| 626 | 
            -
                    if preprocess_include match[1], match[2].strip
         | 
| 627 | 
            -
                      # peek again since the content has changed
         | 
| 628 | 
            -
                      nil
         | 
| 629 | 
            -
                    else
         | 
| 630 | 
            -
                      # the line was not a valid include line and is unchanged
         | 
| 631 | 
            -
                      # mark it as visited and return it
         | 
| 632 | 
            -
                      @look_ahead += 1
         | 
| 633 | 
            -
                      line
         | 
| 634 | 
            -
                    end
         | 
| 635 | 
            -
                  end
         | 
| 644 | 
            +
                  nil
         | 
| 636 645 | 
             
                else
         | 
| 637 | 
            -
                  # optimization to inline super
         | 
| 638 | 
            -
                  #super
         | 
| 646 | 
            +
                  # NOTE optimization to inline super
         | 
| 639 647 | 
             
                  @look_ahead += 1
         | 
| 640 648 | 
             
                  line
         | 
| 641 649 | 
             
                end
         | 
| @@ -685,17 +693,20 @@ class PreprocessorReader < Reader | |
| 685 693 | 
             
                # don't honor match if it doesn't meet this criteria
         | 
| 686 694 | 
             
                # QUESTION should we warn for these bogus declarations?
         | 
| 687 695 | 
             
                if ((directive == 'ifdef' || directive == 'ifndef') && target.empty?) ||
         | 
| 688 | 
            -
                    (directive == 'endif' &&  | 
| 696 | 
            +
                    (directive == 'endif' && text)
         | 
| 689 697 | 
             
                  return false
         | 
| 690 698 | 
             
                end
         | 
| 691 699 |  | 
| 700 | 
            +
                # attributes are case insensitive
         | 
| 701 | 
            +
                target = target.downcase
         | 
| 702 | 
            +
             | 
| 692 703 | 
             
                if directive == 'endif'
         | 
| 693 704 | 
             
                  stack_size = @conditional_stack.size
         | 
| 694 705 | 
             
                  if stack_size > 0
         | 
| 695 | 
            -
                    pair = @conditional_stack | 
| 706 | 
            +
                    pair = @conditional_stack[-1]
         | 
| 696 707 | 
             
                    if target.empty? || target == pair[:target]
         | 
| 697 708 | 
             
                      @conditional_stack.pop
         | 
| 698 | 
            -
                      @skipping = @conditional_stack.empty? ? false : @conditional_stack | 
| 709 | 
            +
                      @skipping = @conditional_stack.empty? ? false : @conditional_stack[-1][:skipping]
         | 
| 699 710 | 
             
                    else
         | 
| 700 711 | 
             
                      warn "asciidoctor: ERROR: #{line_info}: mismatched macro: endif::#{target}[], expected endif::#{pair[:target]}[]"
         | 
| 701 712 | 
             
                    end
         | 
| @@ -736,7 +747,7 @@ class PreprocessorReader < Reader | |
| 736 747 | 
             
                  when 'ifeval'
         | 
| 737 748 | 
             
                    # the text in brackets must match an expression
         | 
| 738 749 | 
             
                    # don't honor match if it doesn't meet this criteria
         | 
| 739 | 
            -
                    if !target.empty? || !(expr_match = text.strip | 
| 750 | 
            +
                    if !target.empty? || !(expr_match = EvalExpressionRx.match(text.strip))
         | 
| 740 751 | 
             
                      return false
         | 
| 741 752 | 
             
                    end
         | 
| 742 753 |  | 
| @@ -750,7 +761,7 @@ class PreprocessorReader < Reader | |
| 750 761 | 
             
                end
         | 
| 751 762 |  | 
| 752 763 | 
             
                # conditional inclusion block
         | 
| 753 | 
            -
                if directive == 'ifeval' || text | 
| 764 | 
            +
                if directive == 'ifeval' || !text
         | 
| 754 765 | 
             
                  @skipping = true if skip
         | 
| 755 766 | 
             
                  @conditional_stack << {:target => target, :skip => skip, :skipping => @skipping}
         | 
| 756 767 | 
             
                # single line conditional inclusion
         | 
| @@ -759,7 +770,7 @@ class PreprocessorReader < Reader | |
| 759 770 | 
             
                    # FIXME slight hack to skip past conditional line
         | 
| 760 771 | 
             
                    # but keep our synthetic line marked as processed
         | 
| 761 772 | 
             
                    conditional_line = peek_line true
         | 
| 762 | 
            -
                    replace_line  | 
| 773 | 
            +
                    replace_line text.rstrip
         | 
| 763 774 | 
             
                    unshift conditional_line
         | 
| 764 775 | 
             
                    return true
         | 
| 765 776 | 
             
                  end
         | 
| @@ -789,11 +800,11 @@ class PreprocessorReader < Reader | |
| 789 800 | 
             
              #          target slot of the include::[] macro
         | 
| 790 801 | 
             
              #
         | 
| 791 802 | 
             
              # returns a Boolean indicating whether the line under the cursor has changed.
         | 
| 792 | 
            -
              def preprocess_include  | 
| 793 | 
            -
                target = @document.sub_attributes  | 
| 794 | 
            -
             | 
| 795 | 
            -
             | 
| 796 | 
            -
                     | 
| 803 | 
            +
              def preprocess_include raw_target, raw_attributes
         | 
| 804 | 
            +
                if (target = @document.sub_attributes raw_target, :attribute_missing => 'drop-line').empty?
         | 
| 805 | 
            +
                  if @document.attributes.fetch('attribute-missing', Compliance.attribute_missing) == 'skip'
         | 
| 806 | 
            +
                    replace_line %(Unresolved directive in #{@path} - include::#{raw_target}[#{raw_attributes}])
         | 
| 807 | 
            +
                    true
         | 
| 797 808 | 
             
                  else
         | 
| 798 809 | 
             
                    advance
         | 
| 799 810 | 
             
                    true
         | 
| @@ -801,26 +812,34 @@ class PreprocessorReader < Reader | |
| 801 812 | 
             
                # assume that if an include processor is given, the developer wants
         | 
| 802 813 | 
             
                # to handle when and how to process the include
         | 
| 803 814 | 
             
                elsif include_processors? &&
         | 
| 804 | 
            -
                    ( | 
| 815 | 
            +
                    (extension = @include_processor_extensions.find {|candidate| candidate.instance.handles? target })
         | 
| 805 816 | 
             
                  advance
         | 
| 806 | 
            -
                  #  | 
| 807 | 
            -
                   | 
| 817 | 
            +
                  # FIXME parse attributes if requested by extension
         | 
| 818 | 
            +
                  extension.process_method[@document, self, target, AttributeList.new(raw_attributes).parse]
         | 
| 808 819 | 
             
                  true
         | 
| 809 820 | 
             
                # if running in SafeMode::SECURE or greater, don't process this directive
         | 
| 810 821 | 
             
                # however, be friendly and at least make it a link to the source document
         | 
| 811 822 | 
             
                elsif @document.safe >= SafeMode::SECURE
         | 
| 812 | 
            -
                   | 
| 813 | 
            -
                   | 
| 814 | 
            -
                  #output_target = %(#{File.join(File.dirname(target), File.basename(target, File.extname(target)))}#{@document.attributes['outfilesuffix']})
         | 
| 815 | 
            -
                  #unshift "link:#{output_target}[]#{::Asciidoctor::EOL}"
         | 
| 823 | 
            +
                  # FIXME we don't want to use a link macro if we are in a verbatim context
         | 
| 824 | 
            +
                  replace_line %(link:#{target}[])
         | 
| 816 825 | 
             
                  true
         | 
| 817 826 | 
             
                elsif (abs_maxdepth = @maxdepth[:abs]) > 0 && @include_stack.size >= abs_maxdepth
         | 
| 818 827 | 
             
                  warn %(asciidoctor: ERROR: #{line_info}: maximum include depth of #{@maxdepth[:rel]} exceeded)
         | 
| 819 828 | 
             
                  false
         | 
| 820 829 | 
             
                elsif abs_maxdepth > 0
         | 
| 821 | 
            -
                  if  | 
| 830 | 
            +
                  if ::RUBY_ENGINE_OPAL
         | 
| 831 | 
            +
                    # NOTE resolves uri relative to currently loaded document
         | 
| 832 | 
            +
                    # NOTE we defer checking if file exists and catch the 404 error if it does not
         | 
| 833 | 
            +
                    # TODO only use this logic if env-browser is set
         | 
| 834 | 
            +
                    target_type = :file
         | 
| 835 | 
            +
                    include_file = path = if @include_stack.empty?
         | 
| 836 | 
            +
                      ::Dir.pwd == @document.base_dir ? target : (::File.join @dir, target)
         | 
| 837 | 
            +
                    else
         | 
| 838 | 
            +
                      ::File.join @dir, target
         | 
| 839 | 
            +
                    end
         | 
| 840 | 
            +
                  elsif target.include?(':') && UriSniffRx =~ target
         | 
| 822 841 | 
             
                    unless @document.attributes.has_key? 'allow-uri-read'
         | 
| 823 | 
            -
                      replace_line  | 
| 842 | 
            +
                      replace_line %(link:#{target}[])
         | 
| 824 843 | 
             
                      return true
         | 
| 825 844 | 
             
                    end
         | 
| 826 845 |  | 
| @@ -830,16 +849,17 @@ class PreprocessorReader < Reader | |
| 830 849 | 
             
                      # caching requires the open-uri-cached gem to be installed
         | 
| 831 850 | 
             
                      # processing will be automatically aborted if these libraries can't be opened
         | 
| 832 851 | 
             
                      Helpers.require_library 'open-uri/cached', 'open-uri-cached'
         | 
| 833 | 
            -
                     | 
| 834 | 
            -
                       | 
| 852 | 
            +
                    elsif !::RUBY_ENGINE_OPAL
         | 
| 853 | 
            +
                      # autoload open-uri
         | 
| 854 | 
            +
                      ::OpenURI
         | 
| 835 855 | 
             
                    end
         | 
| 836 856 | 
             
                  else
         | 
| 837 857 | 
             
                    target_type = :file
         | 
| 838 858 | 
             
                    # include file is resolved relative to dir of current include, or base_dir if within original docfile
         | 
| 839 859 | 
             
                    include_file = @document.normalize_system_path(target, @dir, nil, :target_name => 'include file')
         | 
| 840 | 
            -
                     | 
| 860 | 
            +
                    unless ::File.file? include_file
         | 
| 841 861 | 
             
                      warn "asciidoctor: WARNING: #{line_info}: include file not found: #{include_file}"
         | 
| 842 | 
            -
                       | 
| 862 | 
            +
                      replace_line %(Unresolved directive in #{@path} - include::#{target}[#{raw_attributes}])
         | 
| 843 863 | 
             
                      return true
         | 
| 844 864 | 
             
                    end
         | 
| 845 865 | 
             
                    #path = @document.relative_path include_file
         | 
| @@ -854,14 +874,14 @@ class PreprocessorReader < Reader | |
| 854 874 | 
             
                    attributes = AttributeList.new(raw_attributes).parse
         | 
| 855 875 | 
             
                    if attributes.has_key? 'lines'
         | 
| 856 876 | 
             
                      inc_lines = []
         | 
| 857 | 
            -
                      attributes['lines'].split( | 
| 877 | 
            +
                      attributes['lines'].split(DataDelimiterRx).each do |linedef|
         | 
| 858 878 | 
             
                        if linedef.include?('..')
         | 
| 859 879 | 
             
                          from, to = linedef.split('..').map(&:to_i)
         | 
| 860 880 | 
             
                          if to == -1
         | 
| 861 881 | 
             
                            inc_lines << from
         | 
| 862 882 | 
             
                            inc_lines << 1.0/0.0
         | 
| 863 883 | 
             
                          else
         | 
| 864 | 
            -
                            inc_lines.concat Range.new(from, to).to_a
         | 
| 884 | 
            +
                            inc_lines.concat ::Range.new(from, to).to_a
         | 
| 865 885 | 
             
                          end
         | 
| 866 886 | 
             
                        else
         | 
| 867 887 | 
             
                          inc_lines << linedef.to_i
         | 
| @@ -869,9 +889,9 @@ class PreprocessorReader < Reader | |
| 869 889 | 
             
                      end
         | 
| 870 890 | 
             
                      inc_lines = inc_lines.sort.uniq
         | 
| 871 891 | 
             
                    elsif attributes.has_key? 'tag'
         | 
| 872 | 
            -
                      tags = [attributes['tag']]
         | 
| 892 | 
            +
                      tags = [attributes['tag']].to_set
         | 
| 873 893 | 
             
                    elsif attributes.has_key? 'tags'
         | 
| 874 | 
            -
                      tags = attributes['tags'].split( | 
| 894 | 
            +
                      tags = attributes['tags'].split(DataDelimiterRx).uniq.to_set
         | 
| 875 895 | 
             
                    end
         | 
| 876 896 | 
             
                  end
         | 
| 877 897 | 
             
                  if !inc_lines.nil?
         | 
| @@ -880,11 +900,11 @@ class PreprocessorReader < Reader | |
| 880 900 | 
             
                      inc_line_offset = 0
         | 
| 881 901 | 
             
                      inc_lineno = 0
         | 
| 882 902 | 
             
                      begin
         | 
| 883 | 
            -
                        open(include_file) do |f|
         | 
| 903 | 
            +
                        open(include_file, 'r') do |f|
         | 
| 884 904 | 
             
                          f.each_line do |l|
         | 
| 885 905 | 
             
                            inc_lineno += 1
         | 
| 886 | 
            -
                            take = inc_lines | 
| 887 | 
            -
                            if take.is_a?(Float) && take.infinite?
         | 
| 906 | 
            +
                            take = inc_lines[0]
         | 
| 907 | 
            +
                            if take.is_a?(::Float) && take.infinite?
         | 
| 888 908 | 
             
                              selected.push l
         | 
| 889 909 | 
             
                              inc_line_offset = inc_lineno if inc_line_offset == 0
         | 
| 890 910 | 
             
                            else
         | 
| @@ -898,8 +918,8 @@ class PreprocessorReader < Reader | |
| 898 918 | 
             
                          end
         | 
| 899 919 | 
             
                        end
         | 
| 900 920 | 
             
                      rescue
         | 
| 901 | 
            -
                        warn  | 
| 902 | 
            -
                         | 
| 921 | 
            +
                        warn %(asciidoctor: WARNING: #{line_info}: include #{target_type} not readable: #{include_file})
         | 
| 922 | 
            +
                        replace_line %(Unresolved directive in #{@path} - include::#{target}[#{raw_attributes}])
         | 
| 903 923 | 
             
                        return true
         | 
| 904 924 | 
             
                      end
         | 
| 905 925 | 
             
                      advance
         | 
| @@ -912,23 +932,26 @@ class PreprocessorReader < Reader | |
| 912 932 | 
             
                      inc_line_offset = 0
         | 
| 913 933 | 
             
                      inc_lineno = 0
         | 
| 914 934 | 
             
                      active_tag = nil
         | 
| 935 | 
            +
                      tags_found = ::Set.new
         | 
| 915 936 | 
             
                      begin
         | 
| 916 | 
            -
                        open(include_file) do |f|
         | 
| 937 | 
            +
                        open(include_file, 'r') do |f|
         | 
| 917 938 | 
             
                          f.each_line do |l|
         | 
| 918 939 | 
             
                            inc_lineno += 1
         | 
| 919 940 | 
             
                            # must force encoding here since we're performing String operations on line
         | 
| 920 | 
            -
                            l.force_encoding(::Encoding::UTF_8) if  | 
| 921 | 
            -
                             | 
| 922 | 
            -
             | 
| 941 | 
            +
                            l.force_encoding(::Encoding::UTF_8) if FORCE_ENCODING
         | 
| 942 | 
            +
                            l = l.rstrip
         | 
| 943 | 
            +
                            if active_tag
         | 
| 944 | 
            +
                              if l.end_with?(%(end::#{active_tag}[])) && TagDirectiveRx =~ l
         | 
| 923 945 | 
             
                                active_tag = nil
         | 
| 924 946 | 
             
                              else
         | 
| 925 | 
            -
                                selected.push l
         | 
| 947 | 
            +
                                selected.push l unless l.end_with?('[]') && TagDirectiveRx =~ l
         | 
| 926 948 | 
             
                                inc_line_offset = inc_lineno if inc_line_offset == 0
         | 
| 927 949 | 
             
                              end
         | 
| 928 950 | 
             
                            else
         | 
| 929 951 | 
             
                              tags.each do |tag|
         | 
| 930 | 
            -
                                if l. | 
| 952 | 
            +
                                if l.end_with?(%(tag::#{tag}[])) && TagDirectiveRx =~ l
         | 
| 931 953 | 
             
                                  active_tag = tag
         | 
| 954 | 
            +
                                  tags_found << tag
         | 
| 932 955 | 
             
                                  break
         | 
| 933 956 | 
             
                                end
         | 
| 934 957 | 
             
                              end
         | 
| @@ -936,10 +959,13 @@ class PreprocessorReader < Reader | |
| 936 959 | 
             
                          end
         | 
| 937 960 | 
             
                        end
         | 
| 938 961 | 
             
                      rescue
         | 
| 939 | 
            -
                        warn  | 
| 940 | 
            -
                         | 
| 962 | 
            +
                        warn %(asciidoctor: WARNING: #{line_info}: include #{target_type} not readable: #{include_file})
         | 
| 963 | 
            +
                        replace_line %(Unresolved directive in #{@path} - include::#{target}[#{raw_attributes}])
         | 
| 941 964 | 
             
                        return true
         | 
| 942 965 | 
             
                      end
         | 
| 966 | 
            +
                      unless (missing_tags = tags.to_a - tags_found.to_a).empty?
         | 
| 967 | 
            +
                        warn "asciidoctor: WARNING: #{line_info}: tag#{missing_tags.size > 1 ? 's' : nil} '#{missing_tags * ','}' not found in include #{target_type}: #{include_file}"
         | 
| 968 | 
            +
                      end
         | 
| 943 969 | 
             
                      advance
         | 
| 944 970 | 
             
                      # FIXME not accounting for skipped lines in reader line numbering
         | 
| 945 971 | 
             
                      push_include selected, include_file, path, inc_line_offset, attributes
         | 
| @@ -947,10 +973,10 @@ class PreprocessorReader < Reader | |
| 947 973 | 
             
                  else
         | 
| 948 974 | 
             
                    begin
         | 
| 949 975 | 
             
                      advance
         | 
| 950 | 
            -
                      push_include open(include_file) {|f| f.read }, include_file, path, 1, attributes
         | 
| 976 | 
            +
                      push_include open(include_file, 'r') {|f| f.read }, include_file, path, 1, attributes
         | 
| 951 977 | 
             
                    rescue
         | 
| 952 | 
            -
                      warn  | 
| 953 | 
            -
                       | 
| 978 | 
            +
                      warn %(asciidoctor: WARNING: #{line_info}: include #{target_type} not readable: #{include_file})
         | 
| 979 | 
            +
                      replace_line %(Unresolved directive in #{@path} - include::#{target}[#{raw_attributes}])
         | 
| 954 980 | 
             
                      return true
         | 
| 955 981 | 
             
                    end
         | 
| 956 982 | 
             
                  end
         | 
| @@ -960,31 +986,74 @@ class PreprocessorReader < Reader | |
| 960 986 | 
             
                end
         | 
| 961 987 | 
             
              end
         | 
| 962 988 |  | 
| 989 | 
            +
              # Public: Push source onto the front of the reader and switch the context
         | 
| 990 | 
            +
              # based on the file, document-relative path and line information given.
         | 
| 991 | 
            +
              #
         | 
| 992 | 
            +
              # This method is typically used in an IncludeProcessor to add source
         | 
| 993 | 
            +
              # read from the target specified.
         | 
| 994 | 
            +
              #
         | 
| 995 | 
            +
              # Examples
         | 
| 996 | 
            +
              #
         | 
| 997 | 
            +
              #    path = 'partial.adoc'
         | 
| 998 | 
            +
              #    file = File.expand_path path
         | 
| 999 | 
            +
              #    data = IO.read file
         | 
| 1000 | 
            +
              #    reader.push_include data, file, path
         | 
| 1001 | 
            +
              #
         | 
| 1002 | 
            +
              # Returns nothing
         | 
| 963 1003 | 
             
              def push_include data, file = nil, path = nil, lineno = 1, attributes = {}
         | 
| 964 1004 | 
             
                @include_stack << [@lines, @file, @dir, @path, @lineno, @maxdepth, @process_lines]
         | 
| 965 | 
            -
                 | 
| 966 | 
            -
             | 
| 967 | 
            -
             | 
| 968 | 
            -
             | 
| 1005 | 
            +
                if file
         | 
| 1006 | 
            +
                  @file = file
         | 
| 1007 | 
            +
                  @dir = File.dirname file
         | 
| 1008 | 
            +
                  # only process lines in AsciiDoc files
         | 
| 1009 | 
            +
                  @process_lines = ASCIIDOC_EXTENSIONS[::File.extname(file)]
         | 
| 1010 | 
            +
                else
         | 
| 1011 | 
            +
                  @file = nil
         | 
| 1012 | 
            +
                  @dir = '.' # right?
         | 
| 1013 | 
            +
                  # we don't know what file type we have, so assume AsciiDoc
         | 
| 1014 | 
            +
                  @process_lines = true
         | 
| 1015 | 
            +
                end
         | 
| 1016 | 
            +
             | 
| 1017 | 
            +
                @path = if path
         | 
| 1018 | 
            +
                  @includes << Helpers.rootname(path)
         | 
| 1019 | 
            +
                  path
         | 
| 1020 | 
            +
                else
         | 
| 1021 | 
            +
                  '<stdin>'
         | 
| 1022 | 
            +
                end
         | 
| 1023 | 
            +
             | 
| 969 1024 | 
             
                @lineno = lineno
         | 
| 970 | 
            -
             | 
| 971 | 
            -
                @process_lines = ASCIIDOC_EXTENSIONS[File.extname(@file)]
         | 
| 1025 | 
            +
             | 
| 972 1026 | 
             
                if attributes.has_key? 'depth'
         | 
| 973 1027 | 
             
                  depth = attributes['depth'].to_i
         | 
| 974 1028 | 
             
                  depth = 1 if depth <= 0
         | 
| 975 1029 | 
             
                  @maxdepth = {:abs => (@include_stack.size - 1) + depth, :rel => depth}
         | 
| 976 1030 | 
             
                end
         | 
| 1031 | 
            +
             | 
| 977 1032 | 
             
                # effectively fill the buffer
         | 
| 978 | 
            -
                @lines = prepare_lines data, :condense => false, :indent => attributes['indent']
         | 
| 979 | 
            -
                # FIXME kind of a hack
         | 
| 980 | 
            -
                #Document::AttributeEntry.new('infile', @file).save_to_next_block @document
         | 
| 981 | 
            -
                #Document::AttributeEntry.new('indir', File.dirname(@file)).save_to_next_block @document
         | 
| 982 | 
            -
                if @lines.empty?
         | 
| 1033 | 
            +
                if (@lines = prepare_lines data, :normalize => true, :condense => false, :indent => attributes['indent']).empty?
         | 
| 983 1034 | 
             
                  pop_include
         | 
| 984 1035 | 
             
                else
         | 
| 1036 | 
            +
                  # FIXME we eventually want to handle leveloffset without affecting the lines
         | 
| 1037 | 
            +
                  if attributes.has_key? 'leveloffset'
         | 
| 1038 | 
            +
                    @lines.unshift ''
         | 
| 1039 | 
            +
                    @lines.unshift %(:leveloffset: #{attributes['leveloffset']})
         | 
| 1040 | 
            +
                    @lines.push ''
         | 
| 1041 | 
            +
                    if (old_leveloffset = @document.attr 'leveloffset')
         | 
| 1042 | 
            +
                      @lines.push %(:leveloffset: #{old_leveloffset})
         | 
| 1043 | 
            +
                    else
         | 
| 1044 | 
            +
                      @lines.push ':leveloffset!:'
         | 
| 1045 | 
            +
                    end
         | 
| 1046 | 
            +
                    # compensate for these extra lines
         | 
| 1047 | 
            +
                    @lineno -= 2
         | 
| 1048 | 
            +
                  end
         | 
| 1049 | 
            +
             | 
| 1050 | 
            +
                  # FIXME kind of a hack
         | 
| 1051 | 
            +
                  #Document::AttributeEntry.new('infile', @file).save_to_next_block @document
         | 
| 1052 | 
            +
                  #Document::AttributeEntry.new('indir', @dir).save_to_next_block @document
         | 
| 985 1053 | 
             
                  @eof = false
         | 
| 986 1054 | 
             
                  @look_ahead = 0
         | 
| 987 1055 | 
             
                end
         | 
| 1056 | 
            +
                nil
         | 
| 988 1057 | 
             
              end
         | 
| 989 1058 |  | 
| 990 1059 | 
             
              def pop_include
         | 
| @@ -992,10 +1061,11 @@ class PreprocessorReader < Reader | |
| 992 1061 | 
             
                  @lines, @file, @dir, @path, @lineno, @maxdepth, @process_lines = @include_stack.pop
         | 
| 993 1062 | 
             
                  # FIXME kind of a hack
         | 
| 994 1063 | 
             
                  #Document::AttributeEntry.new('infile', @file).save_to_next_block @document
         | 
| 995 | 
            -
                  #Document::AttributeEntry.new('indir', File.dirname(@file)).save_to_next_block @document
         | 
| 1064 | 
            +
                  #Document::AttributeEntry.new('indir', ::File.dirname(@file)).save_to_next_block @document
         | 
| 996 1065 | 
             
                  @eof = @lines.empty?
         | 
| 997 1066 | 
             
                  @look_ahead = 0
         | 
| 998 1067 | 
             
                end
         | 
| 1068 | 
            +
                nil
         | 
| 999 1069 | 
             
              end
         | 
| 1000 1070 |  | 
| 1001 1071 | 
             
              def include_depth
         | 
| @@ -1025,12 +1095,12 @@ class PreprocessorReader < Reader | |
| 1025 1095 | 
             
              # Private: Ignore front-matter, commonly used in static site generators
         | 
| 1026 1096 | 
             
              def skip_front_matter! data, increment_linenos = true
         | 
| 1027 1097 | 
             
                front_matter = nil
         | 
| 1028 | 
            -
                if data | 
| 1098 | 
            +
                if data[0] == '---'
         | 
| 1029 1099 | 
             
                  original_data = data.dup
         | 
| 1030 1100 | 
             
                  front_matter = []
         | 
| 1031 1101 | 
             
                  data.shift
         | 
| 1032 1102 | 
             
                  @lineno += 1 if increment_linenos
         | 
| 1033 | 
            -
                  while !data.empty? && data | 
| 1103 | 
            +
                  while !data.empty? && data[0] != '---'
         | 
| 1034 1104 | 
             
                    front_matter.push data.shift
         | 
| 1035 1105 | 
             
                    @lineno += 1 if increment_linenos
         | 
| 1036 1106 | 
             
                  end
         | 
| @@ -1115,21 +1185,21 @@ class PreprocessorReader < Reader | |
| 1115 1185 | 
             
              end
         | 
| 1116 1186 |  | 
| 1117 1187 | 
             
              def include_processors?
         | 
| 1118 | 
            -
                if  | 
| 1188 | 
            +
                if !@include_processor_extensions
         | 
| 1119 1189 | 
             
                  if @document.extensions? && @document.extensions.include_processors?
         | 
| 1120 | 
            -
                    @ | 
| 1190 | 
            +
                    @include_processor_extensions = @document.extensions.include_processors
         | 
| 1121 1191 | 
             
                    true
         | 
| 1122 1192 | 
             
                  else
         | 
| 1123 | 
            -
                    @ | 
| 1193 | 
            +
                    @include_processor_extensions = false
         | 
| 1124 1194 | 
             
                    false
         | 
| 1125 1195 | 
             
                  end
         | 
| 1126 1196 | 
             
                else
         | 
| 1127 | 
            -
                  @ | 
| 1197 | 
            +
                  @include_processor_extensions != false
         | 
| 1128 1198 | 
             
                end
         | 
| 1129 1199 | 
             
              end
         | 
| 1130 1200 |  | 
| 1131 1201 | 
             
              def to_s
         | 
| 1132 | 
            -
                %( | 
| 1202 | 
            +
                %(#<#{self.class}@#{object_id} {path: #{@path.inspect}, line #: #{@lineno}, include depth: #{@include_stack.size}, include stack: [#{@include_stack.map {|inc| inc.to_s}.join ', '}]}>)
         | 
| 1133 1203 | 
             
              end
         | 
| 1134 1204 | 
             
            end
         | 
| 1135 1205 | 
             
            end
         |