ruby-changes:61588
From: Yuki <ko1@a...>
Date: Sun, 7 Jun 2020 02:02:28 +0900 (JST)
Subject: [ruby-changes:61588] e5f5446528 (master): Sync did_you_mean
https://git.ruby-lang.org/ruby.git/commit/?id=e5f5446528 From e5f54465284b4505042fca10ace998e1d29c2313 Mon Sep 17 00:00:00 2001 From: Yuki Nishijima <yk.nishijima@g...> Date: Fri, 22 May 2020 17:17:10 -0400 Subject: Sync did_you_mean diff --git a/lib/did_you_mean.rb b/lib/did_you_mean.rb index b8f9257..ab7e6b0 100644 --- a/lib/did_you_mean.rb +++ b/lib/did_you_mean.rb @@ -6,6 +6,7 @@ require_relative 'did_you_mean/spell_checkers/name_error_checkers' https://github.com/ruby/ruby/blob/trunk/lib/did_you_mean.rb#L6 require_relative 'did_you_mean/spell_checkers/method_name_checker' require_relative 'did_you_mean/spell_checkers/key_error_checker' require_relative 'did_you_mean/spell_checkers/null_checker' +require_relative 'did_you_mean/spell_checkers/require_path_checker' require_relative 'did_you_mean/formatters/plain_formatter' require_relative 'did_you_mean/tree_spell_checker' @@ -95,8 +96,9 @@ module DidYouMean https://github.com/ruby/ruby/blob/trunk/lib/did_you_mean.rb#L96 correct_error NameError, NameErrorCheckers correct_error KeyError, KeyErrorChecker correct_error NoMethodError, MethodNameChecker + correct_error LoadError, RequirePathChecker if RUBY_VERSION >= '2.8.0' - # Returns the currenctly set formatter. By default, it is set to +DidYouMean::Formatter+. + # Returns the currently set formatter. By default, it is set to +DidYouMean::Formatter+. def self.formatter @@formatter end diff --git a/lib/did_you_mean/experimental/ivar_name_correction.rb b/lib/did_you_mean/experimental/ivar_name_correction.rb index 322e422..7b97ff4 100644 --- a/lib/did_you_mean/experimental/ivar_name_correction.rb +++ b/lib/did_you_mean/experimental/ivar_name_correction.rb @@ -1,22 +1,11 @@ https://github.com/ruby/ruby/blob/trunk/lib/did_you_mean/experimental/ivar_name_correction.rb#L1 # frozen-string-literal: true -require_relative '../../did_you_mean' +require_relative '../../did_you_mean/spell_checker' +require_relative '../../did_you_mean/spell_checkers/method_name_checker' module DidYouMean module Experimental #:nodoc: - class IvarNameCheckerBuilder #:nodoc: - attr_reader :original_checker - - def initialize(original_checker) #:nodoc: - @original_checker = original_checker - end - - def new(no_method_error) #:nodoc: - IvarNameChecker.new(no_method_error, original_checker: @original_checker) - end - end - - class IvarNameChecker #:nodoc: + class IvarNameChecker < ::DidYouMean::MethodNameChecker #:nodoc: REPLS = { "(irb)" => -> { Readline::HISTORY.to_a.last } } @@ -29,10 +18,10 @@ module DidYouMean https://github.com/ruby/ruby/blob/trunk/lib/did_you_mean/experimental/ivar_name_correction.rb#L18 end end - attr_reader :original_checker + attr_reader :location, :ivar_names - def initialize(no_method_error, original_checker: ) - @original_checker = original_checker.new(no_method_error) + def initialize(no_method_error) + super(no_method_error) @location = no_method_error.backtrace_locations.first @ivar_names = no_method_error.frame_binding.receiver.instance_variables @@ -41,22 +30,22 @@ module DidYouMean https://github.com/ruby/ruby/blob/trunk/lib/did_you_mean/experimental/ivar_name_correction.rb#L30 end def corrections - original_checker.corrections + ivar_name_corrections + super + ivar_name_corrections end def ivar_name_corrections - @ivar_name_corrections ||= SpellChecker.new(dictionary: @ivar_names).correct(receiver_name.to_s) + @ivar_name_corrections ||= SpellChecker.new(dictionary: ivar_names).correct(receiver_name.to_s) end private def receiver_name - return unless @original_checker.receiver.nil? + return unless receiver.nil? - abs_path = @location.absolute_path - lineno = @location.lineno + abs_path = location.absolute_path + lineno = location.lineno - /@(\w+)*\.#{@original_checker.method_name}/ =~ line(abs_path, lineno).to_s && $1 + /@(\w+)*\.#{method_name}/ =~ line(abs_path, lineno).to_s && $1 end def line(abs_path, lineno) @@ -71,6 +60,6 @@ module DidYouMean https://github.com/ruby/ruby/blob/trunk/lib/did_you_mean/experimental/ivar_name_correction.rb#L60 end end - NameError.send(:attr, :frame_binding) - SPELL_CHECKERS['NoMethodError'] = Experimental::IvarNameCheckerBuilder.new(SPELL_CHECKERS['NoMethodError']) + NoMethodError.send(:attr, :frame_binding) + SPELL_CHECKERS['NoMethodError'] = Experimental::IvarNameChecker 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 3a38245..0483127 100644 --- a/lib/did_you_mean/spell_checkers/method_name_checker.rb +++ b/lib/did_you_mean/spell_checkers/method_name_checker.rb @@ -43,7 +43,12 @@ module DidYouMean https://github.com/ruby/ruby/blob/trunk/lib/did_you_mean/spell_checkers/method_name_checker.rb#L43 end def corrections - @corrections ||= SpellChecker.new(dictionary: RB_RESERVED_WORDS + method_names).correct(method_name) - names_to_exclude + @corrections ||= begin + dictionary = method_names + dictionary = RB_RESERVED_WORDS + dictionary if @private_call + + SpellChecker.new(dictionary: dictionary).correct(method_name) - names_to_exclude + end end def method_names diff --git a/lib/did_you_mean/spell_checkers/require_path_checker.rb b/lib/did_you_mean/spell_checkers/require_path_checker.rb new file mode 100644 index 0000000..aaf877b --- /dev/null +++ b/lib/did_you_mean/spell_checkers/require_path_checker.rb @@ -0,0 +1,35 @@ https://github.com/ruby/ruby/blob/trunk/lib/did_you_mean/spell_checkers/require_path_checker.rb#L1 +# frozen-string-literal: true + +require_relative "../spell_checker" +require_relative "../tree_spell_checker" + +module DidYouMean + class RequirePathChecker + attr_reader :path + + INITIAL_LOAD_PATH = $LOAD_PATH.dup.freeze + ENV_SPECIFIC_EXT = ".#{RbConfig::CONFIG["DLEXT"]}" + + private_constant :INITIAL_LOAD_PATH, :ENV_SPECIFIC_EXT + + def self.requireables + @requireables ||= INITIAL_LOAD_PATH + .flat_map {|path| Dir.glob("**/???*{.rb,#{ENV_SPECIFIC_EXT}}", base: path) } + .map {|path| path.chomp!(".rb") || path.chomp!(ENV_SPECIFIC_EXT) } + end + + def initialize(exception) + @path = exception.path + end + + def corrections + @corrections ||= begin + threshold = path.size * 2 + dictionary = self.class.requireables.reject {|str| str.size >= threshold } + spell_checker = path.include?("/") ? TreeSpellChecker : SpellChecker + + spell_checker.new(dictionary: dictionary).correct(path).uniq + end + end + end +end diff --git a/lib/did_you_mean/tree_spell_checker.rb b/lib/did_you_mean/tree_spell_checker.rb index 6a5b485..799f07f 100644 --- a/lib/did_you_mean/tree_spell_checker.rb +++ b/lib/did_you_mean/tree_spell_checker.rb @@ -1,137 +1,109 @@ https://github.com/ruby/ruby/blob/trunk/lib/did_you_mean/tree_spell_checker.rb#L1 +# frozen_string_literal: true + module DidYouMean # spell checker for a dictionary that has a tree # structure, see doc/tree_spell_checker_api.md class TreeSpellChecker - attr_reader :dictionary, :dimensions, :separator, :augment + attr_reader :dictionary, :separator, :augment def initialize(dictionary:, separator: '/', augment: nil) @dictionary = dictionary @separator = separator @augment = augment - @dimensions = parse_dimensions end def correct(input) - plausibles = plausible_dimensions input - return no_idea(input) if plausibles.empty? - suggestions = find_suggestions input, plausibles - return no_idea(input) if suggestions.empty? - suggestions - end + plausibles = plausible_dimensions(input) + return fall_back_to_normal_spell_check(input) if plausibles.empty? - private + suggestions = find_suggestions(input, plausibles) + return fall_back_to_normal_spell_check(input) if suggestions.empty? - def parse_dimensions - ParseDimensions.new(dictionary, separator).call + suggestions end - def find_suggestions(input, plausibles) - states = plausibles[0].product(*plausibles[1..-1]) - paths = possible_paths states - leaf = input.split(separator).last - ideas = find_ideas(paths, leaf) - ideas.compact.flatten + def dictionary_without_leaves + @dictionary_without_leaves ||= dictionary.map { |word| word.split(separator)[0..-2] }.uniq end - def no_idea(input) - return [] unless augment - ::DidYouMean::SpellChecker.new(dictionary: dictionary).correct(input) + def tree_depth + @tree_depth ||= dictionary_without_leaves.max { |a, b| a.size <=> b.size }.size end - def find_ideas(paths, leaf) - paths.map do |path| - names = find_leaves(path) - ideas = CorrectElement.new.call names, leaf - ideas_to_paths ideas, leaf, names, path - end + def dimensions + @dimensions ||= tree_depth.times.map do |index| + dictionary_without_leaves.map { |element| element[index] }.compact.uniq + end end - def ideas_to_paths(ideas, leaf, names, path) - return nil if ideas.empty? - return [path + separator + leaf] if names.include? leaf - ideas.map { |str| path + separator + str } + def find_leaves(path) + path_with_separator = "#{path}#{separator}" + + dictionary + .select {|str| str.include?(path_with_separator) } + .map {|str| str.gsub(path_with_separator, '') } end - def find_leaves(path) - (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/