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



621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
# File 'lib/asciidoctor/reader.rb', line 621

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)



618
619
620
# File 'lib/asciidoctor/reader.rb', line 618

def include_stack
  @include_stack
end

Instance Method Details

#create_include_cursor(file, path, lineno) ⇒ Object



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

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.



644
645
646
# File 'lib/asciidoctor/reader.rb', line 644

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.



755
756
757
# File 'lib/asciidoctor/reader.rb', line 755

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.



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

def has_more_lines?
  peek_line ? true : false
end

#include_depthObject



747
748
749
# File 'lib/asciidoctor/reader.rb', line 747

def include_depth
  @include_stack.size
end

#include_processors?Boolean

Returns:

  • (Boolean)


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

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.



656
657
658
659
660
661
662
663
664
665
# File 'lib/asciidoctor/reader.rb', line 656

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.



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
744
745
# File 'lib/asciidoctor/reader.rb', line 681

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

#shiftObject

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



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

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

#to_sObject



796
797
798
# File 'lib/asciidoctor/reader.rb', line 796

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