class Parser::Source::Buffer


@api public
@return [Integer] first line
First line of the buffer, 1 by default.
@!attribute [r] first_line
@return [String] buffer name
to relative path to the file.
Buffer name. If the buffer was created from a file, the name corresponds
@!attribute [r] name
A source buffer is immutable once populated.
of encoding.
associated location information (name and first line), and takes care
A buffer with source code. {Buffer} contains the source code itself,
#

def self.recognize_encoding(string)

Returns:
  • (String|nil) - encoding name, if recognized

Parameters:
  • string (String) --
def self.recognize_encoding(string)
  return if string.empty?
  # extract the first two lines in an efficient way
  string =~ /\A(.*)\n?(.*\n)?/
  first_line, second_line = $1, $2
  if first_line =~ /\A\xef\xbb\xbf/ # BOM
    return Encoding::UTF_8
  elsif first_line[0, 2] == '#!'
    encoding_line = second_line
  else
    encoding_line = first_line
  end
  if (result = ENCODING_RE.match(encoding_line))
    Encoding.find(result[2] || result[3] || result[5])
  else
    nil
  end
end

def self.reencode_string(input)

Raises:
  • (EncodingError) -

Returns:
  • (String) -

Parameters:
  • input (String) --
def self.reencode_string(input)
  original_encoding = input.encoding
  detected_encoding = recognize_encoding(input.force_encoding(Encoding::BINARY))
  if detected_encoding.nil?
    input.force_encoding(original_encoding)
  elsif detected_encoding == Encoding::BINARY
    input
  else
    input.
      force_encoding(detected_encoding).
      encode(Encoding::UTF_8)
  end
end

def decompose_position(position)

Returns:
  • ([Integer, Integer]) - `[line, column]`

Parameters:
  • position (Integer) --
def decompose_position(position)
  line_no, line_begin = line_for(position)
  [ @first_line + line_no, position - line_begin ]
end

def initialize(name, first_line = 1)

def initialize(name, first_line = 1)
  @name        = name
  @source      = nil
  @first_line  = first_line
  @lines       = nil
  @line_begins = nil
end

def line_begins

def line_begins
  unless @line_begins
    @line_begins, index = [ [ 0, 0 ] ], 1
    @source.each_char do |char|
      if char == NEW_LINE
        @line_begins.unshift [ @line_begins.length, index ]
      end
      index += 1
    end
  end
  @line_begins
end

def line_for(position)

def line_for(position)
  # Fast O(log n) variant for Ruby >=2.0.
  line_begins.bsearch do |line, line_begin|
    line_begin <= position
  end
end

def line_for(position)

def line_for(position)
  # Slower O(n) variant for Ruby <2.0.
  line_begins.find do |line, line_begin|
    line_begin <= position
  end
end

def raw_source=(input)

Returns:
  • (String) -

Raises:
  • (ArgumentError) - if already populated

Parameters:
  • input (String) --
def raw_source=(input)
  if @source
    raise ArgumentError, 'Source::Buffer is immutable'
  end
  @source = input.gsub("\r\n", NEW_LINE).freeze
end

def read

Raises:
  • (ArgumentError) - if already populated

Returns:
  • (Buffer) - self
def read
  File.open(@name, 'rb') do |io|
    self.source = io.read
  end
  self
end

def source

Raises:
  • (RuntimeError) - if buffer is not populated yet

Returns:
  • (String) - source code
def source
  if @source.nil?
    raise RuntimeError, 'Cannot extract source from uninitialized Source::Buffer'
  end
  @source
end

def source=(input)

Returns:
  • (String) -

Raises:
  • (EncodingError) - if `input` includes invalid byte sequence for the encoding
  • (ArgumentError) - if already populated

Parameters:
  • input (String) --
def source=(input)
  if defined?(Encoding)
    input = input.dup if input.frozen?
    input = self.class.reencode_string(input)
    unless input.valid_encoding?
      raise EncodingError, "invalid byte sequence in #{input.encoding.name}"
    end
  end
  self.raw_source = input
end

def source_line(lineno)

Raises:
  • (IndexError) - if `lineno` is out of bounds

Returns:
  • (String) -

Parameters:
  • lineno (Integer) --
def source_line(lineno)
  unless @lines
    @lines = @source.lines.to_a
    @lines.each { |line| line.chomp!(NEW_LINE) }
    # If a file ends with a newline, the EOF token will appear
    # to be one line further than the end of file.
    @lines << ""
  end
  @lines.fetch(lineno - @first_line).dup
end