lib/utils/xt/source_location_extension.rb



module Utils
  # Extension module for adding source location functionality to objects.
  #
  # This module provides enhanced source location capabilities by extending
  # objects with methods that can determine file paths and line numbers
  # associated with method definitions, class references, or file-based
  # locations. It supports parsing of various input formats including file:line
  # syntax, class.method patterns, and provides convenient accessors for
  # filename, line number, and range information through the source_location
  # method.
  module Xt
    # Extension module for adding source location functionality to objects.
    #
    # This module provides enhanced source location capabilities by extending
    # objects with methods that can determine file paths and line numbers
    # associated with method definitions, class references, or file-based
    # locations. It supports parsing of various input formats including
    # file:line syntax, class.method patterns, and provides convenient
    # accessors for filename, line number, and range information through the
    # source_location method.
    module SourceLocationExtension
      # Regular expression to match Ruby class method signatures
      # Matches patterns like "ClassName#method" or "ClassName.method"
      CLASS_METHOD_REGEXP    = /\A([A-Z][\w:]+)([#.])([\w!?]+)/

      # Regular expression to parse file path and line number information
      # Matches patterns like "file.rb:123" or "file.rb:123-125"
      FILE_LINENUMBER_REGEXP = /\A\s*([^:]+):(\d+)-?(\d+)?/

      # The source_location method determines the file path and line number
      # information for an object.
      #
      # This method analyzes the object to extract source location details,
      # handling different cases including string representations that contain
      # file paths with line numbers, class method references, or simple file
      # names. It returns an array containing the filename and line number,
      # along with additional methods attached to the array for convenient
      # access to location information.
      #
      # @return [ Array<String, Integer> ] an array containing the filename and line number,
      #         with additional methods attached for accessing filename, linenumber, and range properties
      def source_location
        filename   = nil
        linenumber = nil
        rangeend   = nil
        if respond_to?(:to_str)
          string = to_str
          case
          when string =~ FILE_LINENUMBER_REGEXP && File.exist?($1)
            filename   = $1
            linenumber = $2.to_i
            rangeend   = $3&.to_i
          when string =~ CLASS_METHOD_REGEXP && !File.exist?(string)
            klassname   = $1
            method_kind = $2 == '#' ? :instance_method : :method
            methodname  = $3
            filename, linenumber = ::Object.const_get(klassname).
              __send__(method_kind, methodname).source_location
          else
            filename = string
          end
        else
          filename = to_s
        end
        array = linenumber ? [ filename, linenumber ] : [ filename, 1 ]
        array_singleton_class = class << array; self; end
        array_singleton_class.instance_eval do
          define_method(:filename) { filename }
          define_method(:linenumber) { linenumber }
          define_method(:rangeend) { rangeend }
          define_method(:to_s) { [ filename, linenumber ].compact * ':' }
          define_method(:range) { rangeend ? "#{to_s}-#{rangeend}" : "#{to_s}" }
        end
        array
      end
    end
  end
end