lib/solargraph/position.rb



# frozen_string_literal: true


module Solargraph
  # The zero-based line and column numbers of a position in a string.

  #

  class Position
    # @return [Integer]

    attr_reader :line

    # @return [Integer]

    attr_reader :character

    alias column character

    # @param line [Integer]

    # @param character [Integer]

    def initialize line, character
      @line = line
      @character = character
    end

    # Get a hash of the position. This representation is suitable for use in

    # the language server protocol.

    #

    # @return [Hash]

    def to_hash
      {
        line: line,
        character: character
      }
    end

    def inspect
      "#<#{self.class} #{line}, #{character}>"
    end

    # Get a numeric offset for the specified text and position.

    #

    # @param text [String]

    # @param position [Position]

    # @return [Integer]

    def self.to_offset text, position
      return 0 if text.empty?
      text.lines[0...position.line].sum(&:length) + position.character
    end

    # Get a numeric offset for the specified text and a position identified

    # by its line and character.

    #

    # @param text [String]

    # @param line [Integer]

    # @param character [Integer]

    # @return [Integer]

    def self.line_char_to_offset text, line, character
      to_offset(text, Position.new(line, character))
    end

    # Get a position for the specified text and offset.

    #

    # @param text [String]

    # @param offset [Integer]

    # @return [Position]

    def self.from_offset text, offset
      cursor = 0
      line = 0
      character = nil
      text.lines.each do |l|
        line_length = l.length
        char_length = l.chomp.length
        if cursor + char_length >= offset
          character = offset - cursor
          break
        end
        cursor += line_length
        line += 1
      end
      character = 0 if character.nil? and (cursor - offset).between?(0, 1)
      raise InvalidOffsetError if character.nil?
      Position.new(line, character)
    end

    # A helper method for generating positions from arrays of integers. The

    # original parameter is returned if it is already a position.

    #

    # @raise [ArgumentError] if the object cannot be converted to a position.

    #

    # @param object [Position, Array(Integer, Integer)]

    # @return [Position]

    def self.normalize object
      return object if object.is_a?(Position)
      return Position.new(object[0], object[1]) if object.is_a?(Array)
      raise ArgumentError, "Unable to convert #{object.class} to Position"
    end

    def == other
      return false unless other.is_a?(Position)
      line == other.line and character == other.character
    end
  end
end