class Asciidoctor::Reader - asciidoctor-1.5.6.2 Documentation

# class Asciidoctor::Reader

Public: Methods for retrieving lines from AsciiDoc source files

### Attributes

dir[R]
file[R]
lineno[R]

Public: Get the 1-based offset of the current line.

path[R]
process_lines[RW]

Public: Control whether lines are processed using #process_line on first visit (default: true)

source_lines[R]

Public: Get the document source as a String Array of lines.

### Public Class Methods

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

Public: Initialize the Reader object

    # File lib/asciidoctor/reader.rb, line 38
    def initialize data = nil, cursor = nil, opts = {}
      if !cursor
        @file = @dir = nil
        @path = '<stdin>'
        @lineno = 1 # IMPORTANT lineno assignment must proceed prepare_lines call!
      elsif ::String === cursor
        @file = cursor
        @dir, @path = ::File.split @file
        @lineno = 1 # IMPORTANT lineno assignment must proceed prepare_lines call!
      else
        @file = cursor.file
        @dir = cursor.dir
        @path = cursor.path || '<stdin>'
        if @file
          unless @dir
            # REVIEW might to look at this assignment closer
            @dir = ::File.dirname @file
            @dir = nil if @dir == '.' # right?
          end

          unless cursor.path
            @path = ::File.basename @file
          end
        end
        @lineno = cursor.lineno || 1 # IMPORTANT lineno assignment must proceed prepare_lines call!
      end
      @lines = data ? (prepare_lines data, opts) : []
      @source_lines = @lines.dup
      @look_ahead = 0
      @process_lines = true
      @unescape_next_line = false
    end

### Public Instance Methods

advance() click to toggle source

Public: Advance to the next line by discarding the line at the front of the stack

Returns a Boolean indicating whether there was a line to discard.

    # File lib/asciidoctor/reader.rb, line 254
    def advance
      shift ? true : false
    end

cursor() click to toggle source

    # File lib/asciidoctor/reader.rb, line 515
    def cursor
      Cursor.new @file, @dir, @path, @lineno
    end

empty?() click to toggle source

Public: Check whether this reader is empty (contains no lines)

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

    # File lib/asciidoctor/reader.rb, line 131
    def empty?
      if @lines.empty?
        @look_ahead = 0
        true
      else
        false
      end
    end

Also aliased as: eof?
eof?()
Alias for: empty?
has_more_lines?() click to toggle source

Public: 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.

    # File lib/asciidoctor/reader.rb, line 119
    def has_more_lines?
      if @lines.empty?
        @look_ahead = 0
        false
      else
        true
      end
    end

line_info() click to toggle source

Public: Get information about the last line read, including file name and line number.

Returns A String summary of the last line read

    # File lib/asciidoctor/reader.rb, line 522
    def line_info
      %Q(#{@path}: line #{@lineno})
    end

Also aliased as: next_line_info
lines() click to toggle source

Public: Get a copy of the remaining Array of String lines managed by this Reader

Returns A copy of the String Array of lines remaining in this Reader

    # File lib/asciidoctor/reader.rb, line 534
    def lines
      @lines.dup
    end

next_line_empty?() click to toggle source

Public: Peek at the next line and check if it's empty (i.e., whitespace only)

This method Does not consume the line from the stack.

Returns True if the there are no more lines or if the next line is empty

    # File lib/asciidoctor/reader.rb, line 146
    def next_line_empty?
      peek_line.nil_or_empty?
    end

next_line_info()
Alias for: line_info
peek_line(direct = false) click to toggle source

Public: Peek at the next line of source data. Processes the line if not already marked as processed, but does not consume it.

This method will probe the reader for more lines. If there is a next line that has not previously been visited, the line is passed to the #process_line method to be initialized. This call gives sub-classes the opportunity to do preprocessing. If the return value of the #process_line is nil, the data is assumed to be changed and #peek_line is invoked again to perform further processing.

If has_more_lines? is called immediately before #peek_line, the direct flag is implicitly true (since the line is flagged as visited).

direct - A Boolean flag to bypasses the check for more lines and immediately

    returns the first element of the internal @lines Array. (default: false)

Returns the next line of the source data as a String if there are lines remaining. Returns nothing if there is no more data.

    # File lib/asciidoctor/reader.rb, line 168
    def peek_line direct = false
      if direct || @look_ahead > 0
        @unescape_next_line ? @lines[0][1..-1] : @lines[0]
      elsif @lines.empty?
        @look_ahead = 0
        nil
      else
        # FIXME the problem with this approach is that we aren't
        # retaining the modified line (hence the @unescape_next_line tweak)
        # perhaps we need a stack of proxied lines
        (line = process_line @lines[0]) ? line : peek_line
      end
    end

peek_lines(num, direct = false) click to toggle source

Public: Peek at the next multiple lines of source data. Processes the lines if not already marked as processed, but does not consume them.

This method delegates to #read_line to process and collect the line, then restores the lines to the stack before returning them. This allows the lines to be processed and marked as such so that subsequent reads will not need to process the lines again.

num - The positive Integer number of lines to peek (must be greater than 0). direct - A Boolean indicating whether processing should be disabled when reading lines

Returns A String Array of the next multiple lines of source data, or an empty Array if there are no more lines in this Reader.

    # File lib/asciidoctor/reader.rb, line 195
    def peek_lines num, direct = false
      old_look_ahead = @look_ahead
      result = []
      num.times do
        if (line = direct ? shift : read_line)
          result << line
        else
          @lineno -= 1 if direct
          break
        end
      end

      unless result.empty?
        unshift_all result
        @look_ahead = old_look_ahead if direct
      end

      result
    end

prepare_lines(data, opts = {}) click to toggle source

Internal: Prepare the lines from the provided data

This method strips whitespace from the end of every line of the source data and appends a LF (i.e., Unix endline). This whitespace substitution is very important to how Asciidoctor works.

Any leading or trailing blank lines are also removed.

data - A String Array of input data to be normalized opts - A Hash of options to control what cleansing is done

Returns The String lines extracted from the data

    # File lib/asciidoctor/reader.rb, line 84
    def prepare_lines data, opts = {}
      if ::String === data
        if opts[:normalize]
          Helpers.normalize_lines_from_string data
        else
          data.split LF, -1
        end
      elsif opts[:normalize]
        Helpers.normalize_lines_array data
      else
        data.dup
      end
    end

prev_line_info() click to toggle source

    # File lib/asciidoctor/reader.rb, line 527
    def prev_line_info
      %Q(#{@path}: line #{@lineno - 1})
    end

process_line(line) click to toggle source

Internal: Processes a previously unvisited line

By default, this method marks the line as processed by incrementing the look_ahead counter and returns the line unmodified.

Returns The String line the Reader should make available to the next invocation of #read_line or nil if the Reader should drop the line, advance to the next line and process it.

    # File lib/asciidoctor/reader.rb, line 107
    def process_line line
      @look_ahead += 1 if @process_lines
      line
    end

read() click to toggle source

Public: Get the remaining lines of source data joined as a String.

Delegates to #read_lines, then joins the result.

Returns the lines read joined as a String

    # File lib/asciidoctor/reader.rb, line 247
    def read
      read_lines * LF
    end

read_line() click to toggle source

Public: Get the next line of source data. Consumes the line returned.

Returns the String of the next line of the source data if data is present. Returns nothing if there is no more data.

    # File lib/asciidoctor/reader.rb, line 219
    def read_line
      # has_more_lines? triggers preprocessor
      shift if @look_ahead > 0 || has_more_lines?
    end

read_lines() click to toggle source

Public: Get the remaining lines of source data.

This method calls #read_line repeatedly until all lines are consumed and returns the lines as a String Array. This method differs from #lines in that it processes each line in turn, hence triggering any preprocessors implemented in sub-classes.

Returns the lines read as a String Array

    # File lib/asciidoctor/reader.rb, line 232
    def read_lines
      lines = []
      # has_more_lines? triggers preprocessor
      while has_more_lines?
        lines << shift
      end
      lines
    end

Also aliased as: readlines
read_lines_until(options = {}) { |line)| ... } click to toggle source

Public: Return all the lines from `@lines` until we (1) run out them,

    (2) find a blank line with :break_on_blank_lines => true, or (3) find
    a line for which the given block evals to true.

options - an optional Hash of processing options:

    * :break_on_blank_lines may be used to specify to break on
        blank lines
    * :skip_first_line may be used to tell the reader to advance
        beyond the first line before beginning the scan
    * :preserve_last_line may be used to specify that the String
        causing the method to stop processing lines should be
        pushed back onto the `lines` Array.
    * :read_last_line may be used to specify that the String
        causing the method to stop processing lines should be
        included in the lines being returned

Returns the Array of lines forming the next segment.

Examples

    data = [
      "First line\n",
      "Second line\n",
      "\n",
      "Third line\n",
    ]
    reader = Reader.new data, nil, :normalize => true

    reader.read_lines_until
    => ["First line", "Second line"]

    # File lib/asciidoctor/reader.rb, line 429
    def read_lines_until options = {}
      result = []
      shift if options[:skip_first_line]
      if @process_lines && options[:skip_processing]
        @process_lines = false
        restore_process_lines = true
      else
        restore_process_lines = false
      end

      if (terminator = options[:terminator])
        break_on_blank_lines = false
        break_on_list_continuation = false
      else
        break_on_blank_lines = options[:break_on_blank_lines]
        break_on_list_continuation = options[:break_on_list_continuation]
      end
      skip_comments = options[:skip_line_comments]
      line_read = false
      line_restored = false

      complete = false
      while !complete && (line = read_line)
        complete = while true
          break true if terminator && line == terminator
          # QUESTION: can we get away with line.empty? here?
          break true if break_on_blank_lines && line.empty?
          if break_on_list_continuation && line_read && line == LIST_CONTINUATION
            options[:preserve_last_line] = true
            break true
          end
          break true if block_given? && (yield line)
          break false
        end

        if complete
          if options[:read_last_line]
            result << line
            line_read = true
          end
          if options[:preserve_last_line]
            unshift line
            line_restored = true
          end
        else
          unless skip_comments && (line.start_with? '//') && !(line.start_with? '///')
            result << line
            line_read = true
          end
        end
      end

      if restore_process_lines
        @process_lines = true
        @look_ahead -= 1 if line_restored && !terminator
      end
      result
    end

readlines()
Alias for: read_lines
replace_line(replacement)

deprecated

Alias for: replace_next_line
replace_next_line(replacement) click to toggle source

Public: Replace the next line with the specified line.

Calls #advance to consume the current line, then calls #unshift to push the replacement onto the top of the line stack.

replacement - The String line to put in place of the next line (i.e., the line at the cursor).

Returns true.

    # File lib/asciidoctor/reader.rb, line 297
    def replace_next_line replacement
      shift
      unshift replacement
      true
    end

Also aliased as: replace_line
restore_line(line_to_restore)
Alias for: unshift_line
restore_lines(lines_to_restore)
Alias for: unshift_lines
shift() click to toggle source

Internal: Shift the line off the stack and increment the lineno

This method can be used directly when you've already called #peek_line and determined that you do, in fact, want to pluck that line off the stack. Use #read_line if the line hasn't (or many not have been) visited yet.

Returns The String line at the top of the stack

    # File lib/asciidoctor/reader.rb, line 495
    def shift
      @lineno += 1
      @look_ahead -= 1 unless @look_ahead == 0
      @lines.shift
    end

skip_blank_lines() click to toggle source

Public: Skip blank lines at the cursor.

Examples

    reader.lines
    => ["", "", "Foo", "Bar", ""]
    reader.skip_blank_lines
    => 2
    reader.lines
    => ["Foo", "Bar", ""]

Returns the [Integer] number of lines skipped or nothing if all lines have been consumed (even if lines were skipped by this method).

    # File lib/asciidoctor/reader.rb, line 318
    def skip_blank_lines
      return if empty?

      num_skipped = 0
      # optimized code for shortest execution path
      while (next_line = peek_line)
        if next_line.empty?
          shift
          num_skipped += 1
        else
          return num_skipped
        end
      end
    end

skip_comment_lines() click to toggle source

Public: Skip consecutive lines containing line comments and return them.

Examples

    @lines
    => ["// foo", "bar"]

    comment_lines = skip_comment_lines
    => ["// foo"]

    @lines
    => ["bar"]

Returns the Array of lines that were skipped

    # File lib/asciidoctor/reader.rb, line 346
    def skip_comment_lines
      return [] if empty?

      comment_lines = []
      while (next_line = peek_line) && !next_line.empty?
        if next_line.start_with? '//'
          if next_line.start_with? '///'
            if (ll = next_line.length) > 3 && next_line == '/' * ll
              comment_lines << shift
              comment_lines.push(*(read_lines_until(:terminator => next_line, :read_last_line => true, :skip_processing => true)))
            else
              break
            end
          else
            comment_lines << shift
          end
        else
          break
        end
      end

      comment_lines
    end

skip_line_comments() click to toggle source

Public: Skip consecutive lines that are line comments and return them.

This method assumes the reader only contains simple lines (no blocks).

    # File lib/asciidoctor/reader.rb, line 373
    def skip_line_comments
      return [] if empty?

      comment_lines = []
      # optimized code for shortest execution path
      while (next_line = peek_line) && !next_line.empty?
        if (next_line.start_with? '//')
          comment_lines << shift
        else
          break
        end
      end

      comment_lines
    end

source() click to toggle source

Public: Get the source lines for this Reader joined as a String

    # File lib/asciidoctor/reader.rb, line 544
    def source
      @source_lines * LF
    end

string() click to toggle source

Public: Get a copy of the remaining lines managed by this Reader joined as a String

    # File lib/asciidoctor/reader.rb, line 539
    def string
      @lines * LF
    end

terminate() click to toggle source

Public: Advance to the end of the reader, consuming all remaining lines

Returns nothing.

    # File lib/asciidoctor/reader.rb, line 392
    def terminate
      @lineno += @lines.size
      @lines.clear
      @look_ahead = 0
      nil
    end

to_s() click to toggle source

Public: Get a summary of this Reader.

Returns A string summary of this reader, which contains the path and line information

    # File lib/asciidoctor/reader.rb, line 552
    def to_s
      line_info
    end

unshift(line) click to toggle source

Internal: Restore the line to the stack and decrement the lineno

    # File lib/asciidoctor/reader.rb, line 502
    def unshift line
      @lineno -= 1
      @look_ahead += 1
      @lines.unshift line
    end

unshift_all(lines) click to toggle source

Internal: Restore the lines to the stack and decrement the lineno

    # File lib/asciidoctor/reader.rb, line 509
    def unshift_all lines
      @lineno -= lines.size
      @look_ahead += lines.size
      @lines.unshift(*lines)
    end

unshift_line(line_to_restore) click to toggle source

Public: Push the String line onto the beginning of the Array of source data.

A line pushed on the reader using this method is not processed again. The method assumes the line was previously retrieved from the reader or does not otherwise contain preprocessor directives. Therefore, it is marked as processed immediately.

line_to_restore - the line to restore onto the stack

Returns nothing.

    # File lib/asciidoctor/reader.rb, line 268
    def unshift_line line_to_restore
      unshift line_to_restore
      nil
    end

Also aliased as: restore_line
unshift_lines(lines_to_restore) click to toggle source

Public: Push an Array of lines onto the front of the Array of source data.

Lines pushed on the reader using this method are not processed again. The method assumes the lines were previously retrieved from the reader or do not otherwise contain preprocessor directives. Therefore, they are marked as processed immediately.

Returns nothing.

    # File lib/asciidoctor/reader.rb, line 282
    def unshift_lines lines_to_restore
      unshift_all lines_to_restore
      nil
    end

Also aliased as: restore_lines