summaryrefslogtreecommitdiff
path: root/lib/did_you_mean
diff options
context:
space:
mode:
Diffstat (limited to 'lib/did_you_mean')
-rw-r--r--lib/did_you_mean/core_ext/name_error.rb57
-rw-r--r--lib/did_you_mean/formatter.rb44
-rw-r--r--lib/did_you_mean/formatters/plain_formatter.rb35
-rw-r--r--lib/did_you_mean/formatters/verbose_formatter.rb49
-rw-r--r--lib/did_you_mean/jaro_winkler.rb7
-rw-r--r--lib/did_you_mean/spell_checker.rb18
-rw-r--r--lib/did_you_mean/spell_checkers/key_error_checker.rb10
-rw-r--r--lib/did_you_mean/spell_checkers/method_name_checker.rb10
-rw-r--r--lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb5
-rw-r--r--lib/did_you_mean/spell_checkers/pattern_key_name_checker.rb28
-rw-r--r--lib/did_you_mean/spell_checkers/require_path_checker.rb5
-rw-r--r--lib/did_you_mean/verbose.rb6
-rw-r--r--lib/did_you_mean/version.rb2
13 files changed, 162 insertions, 114 deletions
diff --git a/lib/did_you_mean/core_ext/name_error.rb b/lib/did_you_mean/core_ext/name_error.rb
index 7e99282b41..8c170c4b90 100644
--- a/lib/did_you_mean/core_ext/name_error.rb
+++ b/lib/did_you_mean/core_ext/name_error.rb
@@ -1,24 +1,49 @@
module DidYouMean
module Correctable
- SKIP_TO_S_FOR_SUPER_LOOKUP = true
- private_constant :SKIP_TO_S_FOR_SUPER_LOOKUP
+ if Exception.method_defined?(:detailed_message)
+ # just for compatibility
+ def original_message
+ # we cannot use alias here because
+ to_s
+ end
+
+ def detailed_message(highlight: true, did_you_mean: true, **)
+ msg = super.dup
+
+ return msg unless did_you_mean
+
+ suggestion = DidYouMean.formatter.message_for(corrections)
+
+ if highlight
+ suggestion = suggestion.gsub(/.+/) { "\e[1m" + $& + "\e[m" }
+ end
- def original_message
- meth = method(:to_s)
- while meth.owner.const_defined?(:SKIP_TO_S_FOR_SUPER_LOOKUP)
- meth = meth.super_method
+ msg << suggestion
+ msg
+ rescue
+ super
+ end
+ else
+ SKIP_TO_S_FOR_SUPER_LOOKUP = true
+ private_constant :SKIP_TO_S_FOR_SUPER_LOOKUP
+
+ def original_message
+ meth = method(:to_s)
+ while meth.owner.const_defined?(:SKIP_TO_S_FOR_SUPER_LOOKUP)
+ meth = meth.super_method
+ end
+ meth.call
end
- meth.call
- end
- def to_s
- msg = super.dup
- suggestion = DidYouMean.formatter.message_for(corrections)
+ def to_s
+ msg = super.dup
+ suggestion = DidYouMean.formatter.message_for(corrections)
- msg << suggestion if !msg.include?(suggestion)
- msg
- rescue
- super
+ msg << suggestion if !msg.include?(suggestion)
+ msg
+ rescue
+ super
+ end
end
def corrections
@@ -26,7 +51,7 @@ module DidYouMean
end
def spell_checker
- SPELL_CHECKERS[self.class.to_s].new(self)
+ DidYouMean.spell_checkers[self.class.to_s].new(self)
end
end
end
diff --git a/lib/did_you_mean/formatter.rb b/lib/did_you_mean/formatter.rb
new file mode 100644
index 0000000000..c43748f707
--- /dev/null
+++ b/lib/did_you_mean/formatter.rb
@@ -0,0 +1,44 @@
+# frozen-string-literal: true
+
+module DidYouMean
+ # The +DidYouMean::Formatter+ is the basic, default formatter for the
+ # gem. The formatter responds to the +message_for+ method and it returns a
+ # human readable string.
+ class Formatter
+
+ # Returns a human readable string that contains +corrections+. This
+ # formatter is designed to be less verbose to not take too much screen
+ # space while being helpful enough to the user.
+ #
+ # @example
+ #
+ # formatter = DidYouMean::Formatter.new
+ #
+ # # displays suggestions in two lines with the leading empty line
+ # puts formatter.message_for(["methods", "method"])
+ #
+ # Did you mean? methods
+ # method
+ # # => nil
+ #
+ # # displays an empty line
+ # puts formatter.message_for([])
+ #
+ # # => nil
+ #
+ def self.message_for(corrections)
+ corrections.empty? ? "" : "\nDid you mean? #{corrections.join("\n ")}"
+ end
+
+ def message_for(corrections)
+ warn "The instance method #message_for has been deprecated. Please use the class method " \
+ "DidYouMean::Formatter.message_for(...) instead."
+
+ self.class.message_for(corrections)
+ end
+ end
+
+ PlainFormatter = Formatter
+
+ deprecate_constant :PlainFormatter
+end
diff --git a/lib/did_you_mean/formatters/plain_formatter.rb b/lib/did_you_mean/formatters/plain_formatter.rb
index e2d995f587..d669588e0f 100644
--- a/lib/did_you_mean/formatters/plain_formatter.rb
+++ b/lib/did_you_mean/formatters/plain_formatter.rb
@@ -1,33 +1,4 @@
-# frozen-string-literal: true
+require_relative '../formatter'
-module DidYouMean
- # The +DidYouMean::PlainFormatter+ is the basic, default formatter for the
- # gem. The formatter responds to the +message_for+ method and it returns a
- # human readable string.
- class PlainFormatter
-
- # Returns a human readable string that contains +corrections+. This
- # formatter is designed to be less verbose to not take too much screen
- # space while being helpful enough to the user.
- #
- # @example
- #
- # formatter = DidYouMean::PlainFormatter.new
- #
- # # displays suggestions in two lines with the leading empty line
- # puts formatter.message_for(["methods", "method"])
- #
- # Did you mean? methods
- # method
- # # => nil
- #
- # # displays an empty line
- # puts formatter.message_for([])
- #
- # # => nil
- #
- def message_for(corrections)
- corrections.empty? ? "" : "\nDid you mean? #{corrections.join("\n ")}"
- end
- end
-end
+warn "`require 'did_you_mean/formatters/plain_formatter'` is deprecated. Please `require 'did_you_mean/formatter'` " \
+ "instead."
diff --git a/lib/did_you_mean/formatters/verbose_formatter.rb b/lib/did_you_mean/formatters/verbose_formatter.rb
index b8fe214d57..f6623681f2 100644
--- a/lib/did_you_mean/formatters/verbose_formatter.rb
+++ b/lib/did_you_mean/formatters/verbose_formatter.rb
@@ -1,49 +1,10 @@
# frozen-string-literal: true
-module DidYouMean
- # The +DidYouMean::VerboseFormatter+ uses extra empty lines to make the
- # suggestion stand out more in the error message.
- #
- # In order to activate the verbose formatter,
- #
- # @example
- #
- # OBject
- # # => NameError: uninitialized constant OBject
- # # Did you mean? Object
- #
- # require 'did_you_mean/verbose'
- #
- # OBject
- # # => NameError: uninitialized constant OBject
- # #
- # # Did you mean? Object
- # #
- #
- class VerboseFormatter
+warn "`require 'did_you_mean/formatters/verbose_formatter'` is deprecated and falls back to the default formatter. "
- # Returns a human readable string that contains +corrections+. This
- # formatter is designed to be less verbose to not take too much screen
- # space while being helpful enough to the user.
- #
- # @example
- #
- # formatter = DidYouMean::PlainFormatter.new
- #
- # puts formatter.message_for(["methods", "method"])
- #
- #
- # Did you mean? methods
- # method
- #
- # # => nil
- #
- def message_for(corrections)
- return "" if corrections.empty?
+require_relative '../formatter'
- output = "\n\n Did you mean? ".dup
- output << corrections.join("\n ")
- output << "\n "
- end
- end
+module DidYouMean
+ # For compatibility:
+ VerboseFormatter = Formatter
end
diff --git a/lib/did_you_mean/jaro_winkler.rb b/lib/did_you_mean/jaro_winkler.rb
index 56db130af4..9a3e57f6d7 100644
--- a/lib/did_you_mean/jaro_winkler.rb
+++ b/lib/did_you_mean/jaro_winkler.rb
@@ -8,8 +8,7 @@ module DidYouMean
m = 0.0
t = 0.0
- range = (length2 / 2).floor - 1
- range = 0 if range < 0
+ range = length2 > 3 ? length2 / 2 - 1 : 0
flags1 = 0
flags2 = 0
@@ -72,10 +71,8 @@ module DidYouMean
codepoints2 = str2.codepoints
prefix_bonus = 0
- i = 0
str1.each_codepoint do |char1|
- char1 == codepoints2[i] && i < 4 ? prefix_bonus += 1 : break
- i += 1
+ char1 == codepoints2[prefix_bonus] && prefix_bonus < 4 ? prefix_bonus += 1 : break
end
jaro_distance + (prefix_bonus * WEIGHT * (1 - jaro_distance))
diff --git a/lib/did_you_mean/spell_checker.rb b/lib/did_you_mean/spell_checker.rb
index e5106abba2..37da2fc7a6 100644
--- a/lib/did_you_mean/spell_checker.rb
+++ b/lib/did_you_mean/spell_checker.rb
@@ -10,25 +10,25 @@ module DidYouMean
end
def correct(input)
- input = normalize(input)
- threshold = input.length > 3 ? 0.834 : 0.77
+ normalized_input = normalize(input)
+ threshold = normalized_input.length > 3 ? 0.834 : 0.77
- words = @dictionary.select { |word| JaroWinkler.distance(normalize(word), input) >= threshold }
- words.reject! { |word| input == word.to_s }
- words.sort_by! { |word| JaroWinkler.distance(word.to_s, input) }
+ words = @dictionary.select { |word| JaroWinkler.distance(normalize(word), normalized_input) >= threshold }
+ words.reject! { |word| input.to_s == word.to_s }
+ words.sort_by! { |word| JaroWinkler.distance(word.to_s, normalized_input) }
words.reverse!
# Correct mistypes
- threshold = (input.length * 0.25).ceil
- corrections = words.select { |c| Levenshtein.distance(normalize(c), input) <= threshold }
+ threshold = (normalized_input.length * 0.25).ceil
+ corrections = words.select { |c| Levenshtein.distance(normalize(c), normalized_input) <= threshold }
# Correct misspells
if corrections.empty?
corrections = words.select do |word|
word = normalize(word)
- length = input.length < word.length ? input.length : word.length
+ length = normalized_input.length < word.length ? normalized_input.length : word.length
- Levenshtein.distance(word, input) < length
+ Levenshtein.distance(word, normalized_input) < length
end.first(1)
end
diff --git a/lib/did_you_mean/spell_checkers/key_error_checker.rb b/lib/did_you_mean/spell_checkers/key_error_checker.rb
index be4bea7789..955bff1be6 100644
--- a/lib/did_you_mean/spell_checkers/key_error_checker.rb
+++ b/lib/did_you_mean/spell_checkers/key_error_checker.rb
@@ -14,7 +14,15 @@ module DidYouMean
private
def exact_matches
- @exact_matches ||= @keys.select { |word| @key == word.to_s }.map(&:inspect)
+ @exact_matches ||= @keys.select { |word| @key == word.to_s }.map { |obj| format_object(obj) }
+ end
+
+ def format_object(symbol_or_object)
+ if symbol_or_object.is_a?(Symbol)
+ ":#{symbol_or_object}"
+ else
+ symbol_or_object.to_s
+ end
end
end
end
diff --git a/lib/did_you_mean/spell_checkers/method_name_checker.rb b/lib/did_you_mean/spell_checkers/method_name_checker.rb
index 0483127d6f..b5cbbb5da6 100644
--- a/lib/did_you_mean/spell_checkers/method_name_checker.rb
+++ b/lib/did_you_mean/spell_checkers/method_name_checker.rb
@@ -6,6 +6,7 @@ module DidYouMean
NAMES_TO_EXCLUDE = { NilClass => nil.methods }
NAMES_TO_EXCLUDE.default = []
+ Ractor.make_shareable(NAMES_TO_EXCLUDE) if defined?(Ractor)
# +MethodNameChecker::RB_RESERVED_WORDS+ is the list of reserved words in
# Ruby that take an argument. Unlike
@@ -36,6 +37,8 @@ module DidYouMean
yield
)
+ Ractor.make_shareable(RB_RESERVED_WORDS) if defined?(Ractor)
+
def initialize(exception)
@method_name = exception.name
@receiver = exception.receiver
@@ -56,6 +59,13 @@ module DidYouMean
method_names = receiver.methods + receiver.singleton_methods
method_names += receiver.private_methods if @private_call
method_names.uniq!
+ # Assume that people trying to use a writer are not interested in a reader
+ # and vice versa
+ if method_name.match?(/=\Z/)
+ method_names.select! { |name| name.match?(/=\Z/) }
+ else
+ method_names.reject! { |name| name.match?(/=\Z/) }
+ end
method_names
else
[]
diff --git a/lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb b/lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb
index 3e51b4fa3a..9a6e04fe64 100644
--- a/lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb
+++ b/lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb
@@ -8,6 +8,7 @@ module DidYouMean
NAMES_TO_EXCLUDE = { 'foo' => [:fork, :for] }
NAMES_TO_EXCLUDE.default = []
+ Ractor.make_shareable(NAMES_TO_EXCLUDE) if defined?(Ractor)
# +VariableNameChecker::RB_RESERVED_WORDS+ is the list of all reserved
# words in Ruby. They could be declared like methods are, and a typo would
@@ -62,6 +63,8 @@ module DidYouMean
__ENCODING__
)
+ Ractor.make_shareable(RB_RESERVED_WORDS) if defined?(Ractor)
+
def initialize(exception)
@name = exception.name.to_s.tr("@", "")
@lvar_names = exception.respond_to?(:local_variables) ? exception.local_variables : []
@@ -76,7 +79,7 @@ module DidYouMean
def corrections
@corrections ||= SpellChecker
.new(dictionary: (RB_RESERVED_WORDS + lvar_names + method_names + ivar_names + cvar_names))
- .correct(name) - NAMES_TO_EXCLUDE[@name]
+ .correct(name).uniq - NAMES_TO_EXCLUDE[@name]
end
end
end
diff --git a/lib/did_you_mean/spell_checkers/pattern_key_name_checker.rb b/lib/did_you_mean/spell_checkers/pattern_key_name_checker.rb
new file mode 100644
index 0000000000..622d4dee25
--- /dev/null
+++ b/lib/did_you_mean/spell_checkers/pattern_key_name_checker.rb
@@ -0,0 +1,28 @@
+require_relative "../spell_checker"
+
+module DidYouMean
+ class PatternKeyNameChecker
+ def initialize(no_matching_pattern_key_error)
+ @key = no_matching_pattern_key_error.key
+ @keys = no_matching_pattern_key_error.matchee.keys
+ end
+
+ def corrections
+ @corrections ||= exact_matches.empty? ? SpellChecker.new(dictionary: @keys).correct(@key).map(&:inspect) : exact_matches
+ end
+
+ private
+
+ def exact_matches
+ @exact_matches ||= @keys.select { |word| @key == word.to_s }.map { |obj| format_object(obj) }
+ end
+
+ def format_object(symbol_or_object)
+ if symbol_or_object.is_a?(Symbol)
+ ":#{symbol_or_object}"
+ else
+ symbol_or_object.to_s
+ end
+ end
+ end
+end
diff --git a/lib/did_you_mean/spell_checkers/require_path_checker.rb b/lib/did_you_mean/spell_checkers/require_path_checker.rb
index e4cdb9f43d..586ced37de 100644
--- a/lib/did_you_mean/spell_checkers/require_path_checker.rb
+++ b/lib/did_you_mean/spell_checkers/require_path_checker.rb
@@ -9,7 +9,10 @@ module DidYouMean
attr_reader :path
INITIAL_LOAD_PATH = $LOAD_PATH.dup.freeze
- ENV_SPECIFIC_EXT = ".#{RbConfig::CONFIG["DLEXT"]}"
+ Ractor.make_shareable(INITIAL_LOAD_PATH) if defined?(Ractor)
+
+ ENV_SPECIFIC_EXT = ".#{RbConfig::CONFIG["DLEXT"]}"
+ Ractor.make_shareable(ENV_SPECIFIC_EXT) if defined?(Ractor)
private_constant :INITIAL_LOAD_PATH, :ENV_SPECIFIC_EXT
diff --git a/lib/did_you_mean/verbose.rb b/lib/did_you_mean/verbose.rb
index 4e86f167ea..1ff19aef80 100644
--- a/lib/did_you_mean/verbose.rb
+++ b/lib/did_you_mean/verbose.rb
@@ -1,4 +1,2 @@
-require_relative '../did_you_mean'
-require_relative 'formatters/verbose_formatter'
-
-DidYouMean.formatter = DidYouMean::VerboseFormatter.new
+warn "The verbose formatter has been removed and now `require 'did_you_mean/verbose'` has no effect. Please " \
+ "remove this call."
diff --git a/lib/did_you_mean/version.rb b/lib/did_you_mean/version.rb
index 8df73e1d62..5745ca1efd 100644
--- a/lib/did_you_mean/version.rb
+++ b/lib/did_you_mean/version.rb
@@ -1,3 +1,3 @@
module DidYouMean
- VERSION = "1.6.0-alpha"
+ VERSION = "1.6.3".freeze
end