class Asciidoctor::Reader
Public: Methods for retrieving lines from Asciidoc documents
def get_line
Returns the String of the next line of the source data if data is present.
Public: Get the next line of source data. Consumes the line returned.
def get_line @lines.shift end
def grab_lines_until(options = {}, &block)
reader.grab_lines_until
"--\n", "\n", "In a different segment\n"]
"Open block\n", "\n", "Can have blank lines\n",
reader = Reader.new ["First paragraph\n", "Second paragraph\n",
Examples
Returns the Array of lines forming the next segment.
pushed back onto the `lines` Array.
causing the method to stop processing lines should be
* :preserve_last_line may be used to specify that the String
blank lines
* :break_on_blank_lines may be used to specify to break on
options - an optional Hash of processing options:
a line for which the given block evals to true.
(2) find a blank line with :break_on_blank_lines => true, or (3) find
Public: Return all the lines from `@lines` until we (1) run out them,
def grab_lines_until(options = {}, &block) buffer = [] while (this_line = self.get_line) Asciidoctor.debug "Processing line: '#{this_line}'" finis ||= true if options[:break_on_blank_lines] && this_line.strip.empty? finis ||= true if block && value = yield(this_line) if finis self.unshift(this_line) if options[:preserve_last_line] break end buffer << this_line end buffer end
def has_lines?
Public: Check whether there are any lines left to read.
def has_lines? @lines.any? end
def initialize(data = [], &block)
data = File.readlines(filename)
Examples
data to include in this document.
block - A block that can be used to retrieve external Asciidoc
data - The Array of Strings holding the Asciidoc source document.
Public: Initialize the Reader object.
def initialize(data = [], &block) raw_source = [] @defines = {} @references = {} data = data.split("\n") if data.is_a? String include_regexp = /^include::([^\[]+)\[\]\s*\n?\z/ data.each do |line| if inc = line.match(include_regexp) if block_given? raw_source << yield(inc[1]) else raw_source.concat(File.readlines(inc[1])) end else raw_source << line end end ifdef_regexp = /^(ifdef|ifndef)::([^\[]+)\[\]/ endif_regexp = /^endif::/ defattr_regexp = /^:([^:!]+):\s*(.*)\s*$/ delete_attr_regexp = /^:([^:]+)!:\s*$/ conditional_regexp = /^\s*\{([^\?]+)\?\s*([^\}]+)\s*\}/ skip_to = nil continuing_value = nil continuing_key = nil @lines = [] raw_source.each do |line| if skip_to skip_to = nil if line.match(skip_to) elsif continuing_value close_continue = false # Lines that start with whitespace and end with a '+' are # a continuation, so gobble them up into `value` if match = line.match(/\s+(.+)\s+\+\s*$/) continuing_value += match[1] elsif match = line.match(/\s+(.+)/) # If this continued line doesn't end with a +, then this # is the end of the continuation, no matter what the next # line does. continuing_value += match[1] close_continue = true else # If this line doesn't start with whitespace, then it's # not a valid continuation line, so push it back for processing close_continue = true raw_source.unshift(line) end if close_continue @defines[continuing_key] = continuing_value continuing_key = nil continuing_value = nil end elsif match = line.match(ifdef_regexp) attr = match[2] skip = case match[1] when 'ifdef'; !@defines.has_key?(attr) when 'ifndef'; @defines.has_key?(attr) end skip_to = /^endif::#{attr}\[\]\s*\n/ if skip elsif match = line.match(defattr_regexp) key = sanitize_attribute_name(match[1]) value = match[2] if match = value.match(Asciidoctor::REGEXP[:attr_continue]) # attribute value continuation line; grab lines until we run out # of continuation lines continuing_key = key continuing_value = match[1] # strip off the spaces and + Asciidoctor.debug "continuing key: #{continuing_key} with partial value: '#{continuing_value}'" else @defines[key] = value Asciidoctor.debug "Defines[#{key}] is '#{value}'" end elsif match = line.match(delete_attr_regexp) key = sanitize_attribute_name(match[1]) @defines.delete(key) elsif !line.match(endif_regexp) while match = line.match(conditional_regexp) value = @defines.has_key?(match[1]) ? match[2] : '' line.sub!(conditional_regexp, value) end @lines << line unless line.match(REGEXP[:comment]) end end # Process bibliography references, so they're available when text # before the reference is being rendered. @lines.each do |line| if biblio = line.match(REGEXP[:biblio]) @references[biblio[1]] = "[#{biblio[1]}]" end end Asciidoctor.debug "About to leave Reader#init, and references is #{@references.inspect}" @source = @lines.join Asciidoctor.debug "Leaving Reader#init, and I have #{@lines.count} lines" Asciidoctor.debug "Also, has_lines? is #{self.has_lines?}" end
def peek_line
Returns a String dup of the next line of the source data if data is present.
Public: Get the next line of source data. Does not consume the line returned.
def peek_line @lines.first.dup if @lines.first end
def sanitize_attribute_name(name)
sanitize_attribute_name('Foo 3 #-Billy')
=> 'foo'
sanitize_attribute_name('foo')
=> 'foobar'
sanitize_attribute_name('Foo Bar')
Examples
Returns a String with the legal name.
name - The String holding the Asciidoc attribute name.
Public: Convert a string to a legal attribute name.
def sanitize_attribute_name(name) name.gsub(/[^\w\-_]/, '').downcase end
def skip_blank
@lines
=> nil
skip_blank
=> ["\n", "\t\n", "Foo\n", "Bar\n", "\n"]
@lines
Examples
Returns nil.
Private: Strip off leading blank lines in the Array of lines.
def skip_blank while @lines.any? && @lines.first.strip.empty? @lines.shift end nil end
def unshift(line)
Public: Push String `line` onto queue of source data lines, unless `line` is nil.
def unshift(line) @lines.unshift(line) if line nil end