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



608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
# File 'lib/asciidoctor/reader.rb', line 608

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)



605
606
607
# File 'lib/asciidoctor/reader.rb', line 605

def include_stack
  @include_stack
end

Instance Method Details

#create_include_cursor(file, path, lineno) ⇒ Object



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

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.



631
632
633
# File 'lib/asciidoctor/reader.rb', line 631

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.



742
743
744
# File 'lib/asciidoctor/reader.rb', line 742

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.



626
627
628
# File 'lib/asciidoctor/reader.rb', line 626

def has_more_lines?
  peek_line ? true : false
end

#include_depthObject



734
735
736
# File 'lib/asciidoctor/reader.rb', line 734

def include_depth
  @include_stack.size
end

#include_processors?Boolean

Returns:

  • (Boolean)


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

def include_processors?
  if @include_processor_extensions.nil?
    if @document.extensions? && (@include_processor_extensions = @document.extensions.include_processors)
      true
    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.



643
644
645
646
647
648
649
650
651
652
# File 'lib/asciidoctor/reader.rb', line 643

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.



668
669
670
671
672
673
674
675
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
# File 'lib/asciidoctor/reader.rb', line 668

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?



750
751
752
753
754
755
756
757
# File 'lib/asciidoctor/reader.rb', line 750

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

#to_sObject



783
784
785
# File 'lib/asciidoctor/reader.rb', line 783

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