class Asciidoctor::PreprocessorReader

Public: Methods for retrieving lines from AsciiDoc source files, evaluating preprocessor directives as each line is read off the Array of lines.

Attributes

include_stack[R]

Public Class Methods

new(document, data = nil, cursor = nil, opts = {}) click to toggle source

Public: Initialize the PreprocessorReader object

Calls superclass method Asciidoctor::Reader::new
# File lib/asciidoctor/reader.rb, line 616
def initialize document, data = nil, cursor = nil, opts = {}
  @document = document
  super data, cursor, opts
  if (default_include_depth = (document.attributes['max-include-depth'] || 64).to_i) > 0
    # track absolute max depth, current max depth for comparing to include stack size, and relative max depth for reporting
    @maxdepth = { abs: default_include_depth, curr: default_include_depth, rel: default_include_depth }
  else
    # if @maxdepth is not set, built-in include functionality is disabled
    @maxdepth = nil
  end
  @include_stack = []
  @includes = document.catalog[:includes]
  @skipping = false
  @conditional_stack = []
  @include_processor_extensions = nil
end

Public Instance Methods

create_include_cursor(file, path, lineno) click to toggle source
# File lib/asciidoctor/reader.rb, line 779
def create_include_cursor file, path, lineno
  if ::String === file
    dir = ::File.dirname file
  elsif RUBY_ENGINE_OPAL
    dir = ::File.dirname(file = file.to_s)
  else
    dir = (dir = ::File.dirname file.path) == '' ? '/' : dir
    file = file.to_s
  end
  Cursor.new file, dir, path, lineno
end
empty?() click to toggle source

(see Reader#empty?)

# File lib/asciidoctor/reader.rb, line 639
def empty?
  peek_line ? false : true
end
Also aliased as: eof?
eof?()
Alias for: empty?
exceeded_max_depth?()
Alias for: exceeds_max_depth?
exceeds_max_depth?() click to toggle source

Public: Reports whether pushing an include on the include stack exceeds the max include depth.

Returns nil if no max depth is set and includes are disabled (max-include-depth=0), false if the current max depth will not be exceeded, and the relative max include depth if the current max depth will be exceed.

# File lib/asciidoctor/reader.rb, line 750
def exceeds_max_depth?
  @maxdepth && @include_stack.size >= @maxdepth[:curr] && @maxdepth[:rel]
end
Also aliased as: exceeded_max_depth?
has_more_lines?() click to toggle source

(see Reader#has_more_lines?)

# File lib/asciidoctor/reader.rb, line 634
def has_more_lines?
  peek_line ? true : false
end
include_depth() click to toggle source
# File lib/asciidoctor/reader.rb, line 742
def include_depth
  @include_stack.size
end
include_processors?() click to toggle source
# File lib/asciidoctor/reader.rb, line 767
def include_processors?
  if @include_processor_extensions.nil?
    if @document.extensions? && @document.extensions.include_processors?
      !!(@include_processor_extensions = @document.extensions.include_processors)
    else
      @include_processor_extensions = false
    end
  else
    @include_processor_extensions != false
  end
end
peek_line(direct = false) click to toggle source

Public: Override the Reader#peek_line method to pop the include stack if the last line has been reached and there’s at least one include on the stack.

Returns the next line of the source data as a String if there are lines remaining in the current include context or a parent include context. Returns nothing if there are no more lines remaining and the include stack is empty.

Calls superclass method Asciidoctor::Reader#peek_line
# File lib/asciidoctor/reader.rb, line 651
def peek_line direct = false
  if (line = super)
    line
  elsif @include_stack.empty?
    nil
  else
    pop_include
    peek_line direct
  end
end
push_include(data, file = nil, path = nil, lineno = 1, attributes = {}) click to toggle source

Public: Push source onto the front of the reader and switch the context based on the file, document-relative path and line information given.

This method is typically used in an IncludeProcessor to add source read from the target specified.

Examples

path = 'partial.adoc'
file = File.expand_path path
data = File.read file
reader.push_include data, file, path

Returns this Reader object.

# File lib/asciidoctor/reader.rb, line 676
def push_include data, file = nil, path = nil, lineno = 1, attributes = {}
  @include_stack << [@lines, @file, @dir, @path, @lineno, @maxdepth, @process_lines]
  if (@file = file)
    # NOTE if file is not a string, assume it's a URI
    if ::String === file
      @dir = ::File.dirname file
    elsif RUBY_ENGINE_OPAL
      @dir = ::URI.parse ::File.dirname(file = file.to_s)
    else
      # NOTE this intentionally throws an error if URI has no path
      (@dir = file.dup).path = (dir = ::File.dirname file.path) == '/' ? '' : dir
      file = file.to_s
    end
    @path = (path ||= ::File.basename file)
    # only process lines in AsciiDoc files
    if (@process_lines = file.end_with?(*ASCIIDOC_EXTENSIONS.keys))
      # NOTE registering the include with a nil value tracks it while not making it visible to interdocument xrefs
      @includes[path.slice 0, (path.rindex '.')] ||= attributes['partial-option'] ? nil : true
    end
  else
    @dir = '.'
    # we don't know what file type we have, so assume AsciiDoc
    @process_lines = true
    if (@path = path)
      # NOTE registering the include with a nil value tracks it while not making it visible to interdocument xrefs
      @includes[Helpers.rootname path] ||= attributes['partial-option'] ? nil : true
    else
      @path = '<stdin>'
    end
  end

  @lineno = lineno

  if @maxdepth && (attributes.key? 'depth')
    if (rel_maxdepth = attributes['depth'].to_i) > 0
      if (curr_maxdepth = @include_stack.size + rel_maxdepth) > (abs_maxdepth = @maxdepth[:abs])
        # if relative depth exceeds absolute max depth, effectively ignore relative depth request
        curr_maxdepth = rel_maxdepth = abs_maxdepth
      end
      @maxdepth = { abs: abs_maxdepth, curr: curr_maxdepth, rel: rel_maxdepth }
    else
      @maxdepth = { abs: @maxdepth[:abs], curr: @include_stack.size, rel: 0 }
    end
  end

  # effectively fill the buffer
  if (@lines = prepare_lines data, normalize: @process_lines || :chomp, condense: false, indent: attributes['indent']).empty?
    pop_include
  else
    # FIXME we eventually want to handle leveloffset without affecting the lines
    if attributes.key? 'leveloffset'
      @lines = [((leveloffset = @document.attr 'leveloffset') ? %(:leveloffset: #{leveloffset}) : ':leveloffset!:'), ''] + @lines.reverse + ['', %(:leveloffset: #{attributes['leveloffset']})]
      # compensate for these extra lines at the top
      @lineno -= 2
    else
      @lines.reverse!
    end

    # FIXME kind of a hack
    #Document::AttributeEntry.new('infile', @file).save_to_next_block @document
    #Document::AttributeEntry.new('indir', @dir).save_to_next_block @document
    @look_ahead = 0
  end
  self
end
shift() click to toggle source

TODO Document this override also, we now have the field in the super class, so perhaps just implement the logic there?

Calls superclass method Asciidoctor::Reader#shift
# File lib/asciidoctor/reader.rb, line 758
def shift
  if @unescape_next_line
    @unescape_next_line = false
    (line = super).slice 1, line.length
  else
    super
  end
end
to_s() click to toggle source
# File lib/asciidoctor/reader.rb, line 791
def to_s
  %(#<#{self.class}@#{object_id} {path: #{@path.inspect}, line: #{@lineno}, include depth: #{@include_stack.size}, include stack: [#{@include_stack.map {|inc| inc.to_s }.join ', '}]}>)
end