piotrmurach/tty-prompt

View on GitHub
lib/tty/prompt/suggestion.rb

Summary

Maintainability
A
0 mins
Test Coverage
# frozen_string_literal: true

require_relative "distance"

module TTY
  # A class responsible for terminal prompt interactions.
  class Prompt
    # A class representing a suggestion out of possible choices
    #
    # @api public
    class Suggestion
      DEFAULT_INDENT = 8

      SINGLE_TEXT    = "Did you mean this?"

      PLURAL_TEXT    = "Did you mean one of these?"

      # Number of spaces
      #
      # @api public
      attr_reader :indent

      # Text for a single suggestion
      #
      # @api public
      attr_reader :single_text

      # Text for multiple suggestions
      #
      # @api public
      attr_reader :plural_text

      # Initialize a Suggestion
      #
      # @api public
      def initialize(**options)
        @indent      = options.fetch(:indent) { DEFAULT_INDENT }
        @single_text = options.fetch(:single_text) { SINGLE_TEXT }
        @plural_text = options.fetch(:plural_text) { PLURAL_TEXT }
        @suggestions = []
        @comparator  = Distance.new
      end

      # Suggest matches out of possibile strings
      #
      # @param [String] message
      #
      # @param [Array[String]] possibilities
      #
      # @api public
      def suggest(message, possibilities)
        distances        = measure_distances(message, possibilities)
        minimum_distance = distances.keys.min
        max_distance     = distances.keys.max

        if minimum_distance < max_distance
          @suggestions = distances[minimum_distance].sort
        end
        evaluate
      end

      private

      # Measure distances between messag and possibilities
      #
      # @param [String] message
      #
      # @param [Array[String]] possibilities
      #
      # @return [Hash]
      #
      # @api private
      def measure_distances(message, possibilities)
        distances = Hash.new { |hash, key| hash[key] = [] }

        possibilities.each do |possibility|
          distances[@comparator.distance(message, possibility)] << possibility
        end
        distances
      end

      # Build up a suggestion string
      #
      # @param [Array[String]] suggestions
      #
      # @return [String]
      #
      # @api private
      def evaluate
        return @suggestions if @suggestions.empty?

        if @suggestions.one?
          build_single_suggestion
        else
          build_multiple_suggestions
        end
      end

      # @api private
      def build_single_suggestion
        single_text + "\n" + (" " * indent) + @suggestions.first
      end

      # @api private
      def build_multiple_suggestions
        plural_text + "\n" +
        @suggestions.map do |sugest|
          " " * indent + sugest
        end.join("\n")
      end
    end # Suggestion
  end # Prompt
end # TTY