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)
-
(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)
-
(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)
-
([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 line_range(lineno)
-
(IndexError)
- if `lineno` is out of bounds
Returns:
-
(Range)
-
Parameters:
-
lineno
(Integer
) --
def line_range(lineno) index = lineno - @first_line + 1 if index <= 0 || index > line_begins.size raise IndexError, 'Parser::Source::Buffer: range for line ' \ "#{lineno} requested, valid line numbers are #{@first_line}.." \ "#{@first_line + line_begins.size - 1}" elsif index == line_begins.size Range.new(self, line_begins[-index][1], @source.size) else Range.new(self, line_begins[-index][1], line_begins[-index - 1][1] - 1) end end
def raw_source=(input)
-
(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
-
(ArgumentError)
- if already populated
Returns:
-
(Buffer)
- self
def read File.open(@name, 'rb') do |io| self.source = io.read end self end
def source
-
(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)
-
(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)
-
(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