Class: Asciidoctor::PreprocessorReader

Inherits:
Reader
  • Object
show all
Defined in:
lib/asciidoctor/reader.rb

Overview

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

Instance Attribute Summary collapse

Attributes inherited from Reader

#dir, #file, #lineno, #path, #process_lines, #source_lines, #unterminated

Instance Method Summary collapse

Methods inherited from Reader

#advance, #cursor, #cursor_at_line, #cursor_at_mark, #cursor_at_prev_line, #cursor_before_mark, #line_info, #lines, #mark, #next_line_empty?, #peek_lines, #read, #read_line, #read_lines, #read_lines_until, #replace_next_line, #skip_blank_lines, #skip_comment_lines, #skip_line_comments, #source, #string, #terminate, #unshift_line, #unshift_lines

Methods included from Logging

#logger, #message_with_context

Constructor Details

#initialize(document, data = nil, cursor = nil, opts = {}) ⇒ PreprocessorReader

Initialize the PreprocessorReader object



616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
# 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

Instance Attribute Details

#include_stackObject (readonly)



613
614
615
# File 'lib/asciidoctor/reader.rb', line 613

def include_stack
  @include_stack
end

Instance Method Details

#create_include_cursor(file, path, lineno) ⇒ Object



782
783
784
785
786
787
788
789
790
791
792
# File 'lib/asciidoctor/reader.rb', line 782

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?Boolean Also known as: eof?

Check whether this reader is empty (contains no lines)

Returns:

  • (Boolean)

    true if there are no more lines to peek, otherwise false.



639
640
641
# File 'lib/asciidoctor/reader.rb', line 639

def empty?
  peek_line ? false : true
end

#exceeds_max_depth?Boolean Also known as: exceeded_max_depth?

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

Returns:

  • (Boolean)

    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.



753
754
755
# File 'lib/asciidoctor/reader.rb', line 753

def exceeds_max_depth?
  @maxdepth && @include_stack.size >= @maxdepth[:curr] && @maxdepth[:rel]
end

#has_more_lines?True

Check whether there are any lines left to read.

If a previous call to this method resulted in a value of false, immediately returned the cached value. Otherwise, delegate to peek_line to determine if there is a next line available.

Returns:

  • (True)

    if there are more lines, False if there are not.



634
635
636
# File 'lib/asciidoctor/reader.rb', line 634

def has_more_lines?
  peek_line ? true : false
end

#include_depthObject



745
746
747
# File 'lib/asciidoctor/reader.rb', line 745

def include_depth
  @include_stack.size
end

#include_processors?Boolean

Returns:

  • (Boolean)


770
771
772
773
774
775
776
777
778
779
780
# File 'lib/asciidoctor/reader.rb', line 770

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) ⇒ Object

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.

  • nothing if there are no more lines remaining and the include stack is empty.



651
652
653
654
655
656
657
658
659
660
# 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 = {}) ⇒ Object

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.



676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
# 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))
      @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)
      @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: true, 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.unshift ''
      @lines.unshift %(:leveloffset: #{attributes['leveloffset']})
      @lines << ''
      if (old_leveloffset = @document.attr 'leveloffset')
        @lines << %(:leveloffset: #{old_leveloffset})
      else
        @lines << ':leveloffset!:'
      end
      # compensate for these extra lines
      @lineno -= 2
    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

#shiftObject

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



761
762
763
764
765
766
767
768
# File 'lib/asciidoctor/reader.rb', line 761

def shift
  if @unescape_next_line
    @unescape_next_line = false
    (line = super).slice 1, line.length
  else
    super
  end
end

#to_sObject



794
795
796
# File 'lib/asciidoctor/reader.rb', line 794

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