зеркало из https://github.com/github/ruby.git
Sync did_you_mean
This commit is contained in:
Родитель
0c00a4176b
Коммит
e5f5446528
|
@ -6,6 +6,7 @@ require_relative 'did_you_mean/spell_checkers/name_error_checkers'
|
|||
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
|
|||
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
|
||||
|
|
|
@ -1,22 +1,11 @@
|
|||
# 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
|
|||
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
|
|||
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
|
|||
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
|
||||
|
|
|
@ -43,7 +43,12 @@ module DidYouMean
|
|||
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
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
# 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
|
|
@ -1,137 +1,109 @@
|
|||
# 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?
|
||||
plausibles = plausible_dimensions(input)
|
||||
return fall_back_to_normal_spell_check(input) if plausibles.empty?
|
||||
|
||||
suggestions = find_suggestions(input, plausibles)
|
||||
return fall_back_to_normal_spell_check(input) if suggestions.empty?
|
||||
|
||||
suggestions
|
||||
end
|
||||
|
||||
def dictionary_without_leaves
|
||||
@dictionary_without_leaves ||= dictionary.map { |word| word.split(separator)[0..-2] }.uniq
|
||||
end
|
||||
|
||||
def tree_depth
|
||||
@tree_depth ||= dictionary_without_leaves.max { |a, b| a.size <=> b.size }.size
|
||||
end
|
||||
|
||||
def dimensions
|
||||
@dimensions ||= tree_depth.times.map do |index|
|
||||
dictionary_without_leaves.map { |element| element[index] }.compact.uniq
|
||||
end
|
||||
end
|
||||
|
||||
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 plausible_dimensions(input)
|
||||
input.split(separator)[0..-2]
|
||||
.map
|
||||
.with_index { |element, index| correct_element(dimensions[index], element) if dimensions[index] }
|
||||
.compact
|
||||
end
|
||||
|
||||
def possible_paths(states)
|
||||
states.map { |state| state.join(separator) }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parse_dimensions
|
||||
ParseDimensions.new(dictionary, separator).call
|
||||
end
|
||||
|
||||
def find_suggestions(input, plausibles)
|
||||
states = plausibles[0].product(*plausibles[1..-1])
|
||||
paths = possible_paths states
|
||||
paths = possible_paths(states)
|
||||
leaf = input.split(separator).last
|
||||
ideas = find_ideas(paths, leaf)
|
||||
ideas.compact.flatten
|
||||
|
||||
find_ideas(paths, leaf)
|
||||
end
|
||||
|
||||
def no_idea(input)
|
||||
def fall_back_to_normal_spell_check(input)
|
||||
return [] unless augment
|
||||
|
||||
::DidYouMean::SpellChecker.new(dictionary: dictionary).correct(input)
|
||||
end
|
||||
|
||||
def find_ideas(paths, leaf)
|
||||
paths.map do |path|
|
||||
paths.flat_map do |path|
|
||||
names = find_leaves(path)
|
||||
ideas = CorrectElement.new.call names, leaf
|
||||
ideas_to_paths ideas, leaf, names, path
|
||||
end
|
||||
ideas = correct_element(names, leaf)
|
||||
|
||||
ideas_to_paths(ideas, leaf, names, path)
|
||||
end.compact
|
||||
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 }
|
||||
end
|
||||
|
||||
def find_leaves(path)
|
||||
dictionary.map do |str|
|
||||
next unless str.include? "#{path}#{separator}"
|
||||
str.gsub("#{path}#{separator}", '')
|
||||
end.compact
|
||||
end
|
||||
|
||||
def possible_paths(states)
|
||||
states.map do |state|
|
||||
state.join separator
|
||||
if ideas.empty?
|
||||
nil
|
||||
elsif names.include?(leaf)
|
||||
["#{path}#{separator}#{leaf}"]
|
||||
else
|
||||
ideas.map {|str| "#{path}#{separator}#{str}" }
|
||||
end
|
||||
end
|
||||
|
||||
def plausible_dimensions(input)
|
||||
elements = input.split(separator)[0..-2]
|
||||
elements.each_with_index.map do |element, i|
|
||||
next if dimensions[i].nil?
|
||||
CorrectElement.new.call dimensions[i], element
|
||||
end.compact
|
||||
end
|
||||
end
|
||||
|
||||
# parses the elements in each dimension
|
||||
class ParseDimensions
|
||||
def initialize(dictionary, separator)
|
||||
@dictionary = dictionary
|
||||
@separator = separator
|
||||
end
|
||||
|
||||
def call
|
||||
leafless = remove_leaves
|
||||
dimensions = find_elements leafless
|
||||
dimensions.map do |elements|
|
||||
elements.to_set.to_a
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def remove_leaves
|
||||
dictionary.map do |a|
|
||||
elements = a.split(separator)
|
||||
elements[0..-2]
|
||||
end.to_set.to_a
|
||||
end
|
||||
|
||||
def find_elements(leafless)
|
||||
max_elements = leafless.map(&:size).max
|
||||
dimensions = Array.new(max_elements) { [] }
|
||||
(0...max_elements).each do |i|
|
||||
leafless.each do |elements|
|
||||
dimensions[i] << elements[i] unless elements[i].nil?
|
||||
end
|
||||
end
|
||||
dimensions
|
||||
end
|
||||
|
||||
attr_reader :dictionary, :separator
|
||||
end
|
||||
|
||||
# identifies the elements close to element
|
||||
class CorrectElement
|
||||
def initialize
|
||||
end
|
||||
|
||||
def call(names, element)
|
||||
def correct_element(names, element)
|
||||
return names if names.size == 1
|
||||
str = normalize element
|
||||
return [str] if names.include? str
|
||||
checker = ::DidYouMean::SpellChecker.new(dictionary: names)
|
||||
checker.correct(str)
|
||||
|
||||
str = normalize(element)
|
||||
|
||||
return [str] if names.include?(str)
|
||||
|
||||
::DidYouMean::SpellChecker.new(dictionary: names).correct(str)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def normalize(leaf)
|
||||
str = leaf.dup
|
||||
def normalize(str)
|
||||
str.downcase!
|
||||
return str unless str.include? '@'
|
||||
str.tr!('@', ' ')
|
||||
str.tr!('@', ' ') if str.include?('@')
|
||||
str
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -137,4 +137,11 @@ class MethodNameCheckTest < Test::Unit::TestCase
|
|||
assert_correction :yield, error.corrections
|
||||
assert_match "Did you mean? yield", error.to_s
|
||||
end
|
||||
|
||||
def test_does_not_suggest_yield
|
||||
error = assert_raise(NoMethodError) { 1.yeild }
|
||||
|
||||
assert_correction [], error.corrections
|
||||
assert_not_match(/Did you mean\? +yield/, error.to_s)
|
||||
end if RUBY_ENGINE != "jruby"
|
||||
end
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
require_relative '../helper'
|
||||
|
||||
return if !(RUBY_VERSION >= '2.8.0')
|
||||
|
||||
class RequirePathCheckTest < Test::Unit::TestCase
|
||||
include DidYouMean::TestHelper
|
||||
|
||||
def test_load_error_from_require_has_suggestions
|
||||
error = assert_raise LoadError do
|
||||
require 'open_struct'
|
||||
end
|
||||
|
||||
assert_correction 'ostruct', error.corrections
|
||||
assert_match "Did you mean? ostruct", error.to_s
|
||||
end
|
||||
|
||||
def test_load_error_from_require_for_nested_files_has_suggestions
|
||||
error = assert_raise LoadError do
|
||||
require 'net/htt'
|
||||
end
|
||||
|
||||
assert_correction 'net/http', error.corrections
|
||||
assert_match "Did you mean? net/http", error.to_s
|
||||
|
||||
error = assert_raise LoadError do
|
||||
require 'net-http'
|
||||
end
|
||||
|
||||
assert_correction ['net/http', 'net/https'], error.corrections
|
||||
assert_match "Did you mean? net/http", error.to_s
|
||||
end
|
||||
end
|
|
@ -1,11 +1,12 @@
|
|||
require 'set'
|
||||
require 'yaml'
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative './helper'
|
||||
require "yaml"
|
||||
|
||||
require_relative "./helper"
|
||||
|
||||
class TreeSpellCheckerTest < Test::Unit::TestCase
|
||||
MINI_DIRECTORIES = YAML.load_file(File.expand_path('fixtures/mini_dir.yml', __dir__))
|
||||
RSPEC_DIRECTORIES = YAML.load_file(File.expand_path('fixtures/rspec_dir.yml', __dir__))
|
||||
MINI_DIRECTORIES = YAML.load_file(File.expand_path("fixtures/mini_dir.yml", __dir__))
|
||||
RSPEC_DIRECTORIES = YAML.load_file(File.expand_path("fixtures/rspec_dir.yml", __dir__))
|
||||
|
||||
def setup
|
||||
@dictionary =
|
||||
|
@ -20,154 +21,150 @@ class TreeSpellCheckerTest < Test::Unit::TestCase
|
|||
spec/models/gfsga_spec.rb
|
||||
spec/controllers/vixen_controller_spec.rb
|
||||
)
|
||||
@test_str = 'spek/modeks/confirns/viken_spec.rb'
|
||||
@tsp = DidYouMean::TreeSpellChecker.new(dictionary: @dictionary)
|
||||
@test_str = "spek/modeks/confirns/viken_spec.rb"
|
||||
@tree_spell_checker = DidYouMean::TreeSpellChecker.new(dictionary: @dictionary)
|
||||
end
|
||||
|
||||
def test_corrupt_root
|
||||
word = 'test/verbose_formatter_test.rb'
|
||||
word_error = 'btets/cverbose_formatter_etst.rb suggestions'
|
||||
tsp = DidYouMean::TreeSpellChecker.new(dictionary: MINI_DIRECTORIES)
|
||||
s = tsp.correct(word_error).first
|
||||
assert_match s, word
|
||||
assert_tree_spell "test/verbose_formatter_test.rb",
|
||||
input: "btets/cverbose_formatter_etst.rb suggestions",
|
||||
dictionary: MINI_DIRECTORIES
|
||||
end
|
||||
|
||||
def test_leafless_state
|
||||
tsp = DidYouMean::TreeSpellChecker.new(dictionary: @dictionary.push('spec/features'))
|
||||
word = 'spec/modals/confirms/efgh_spec.rb'
|
||||
word_error = 'spec/modals/confirXX/efgh_spec.rb'
|
||||
s = tsp.correct(word_error).first
|
||||
assert_equal s, word
|
||||
s = tsp.correct('spec/featuresXX')
|
||||
assert_equal 'spec/features', s.first
|
||||
assert_tree_spell "spec/modals/confirms/efgh_spec.rb",
|
||||
input: "spec/modals/confirXX/efgh_spec.rb",
|
||||
dictionary: [*@dictionary, "spec/features"]
|
||||
|
||||
assert_tree_spell "spec/features",
|
||||
input: "spec/featuresXX",
|
||||
dictionary: [*@dictionary, "spec/features"]
|
||||
end
|
||||
|
||||
def test_rake_dictionary
|
||||
dict = %w(parallel:prepare parallel:create parallel:rake parallel:migrate)
|
||||
word_error = 'parallel:preprare'
|
||||
tsp = DidYouMean::TreeSpellChecker.new(dictionary: dict, separator: ':')
|
||||
s = tsp.correct(word_error).first
|
||||
assert_match s, 'parallel:prepare'
|
||||
assert_tree_spell "parallel:prepare",
|
||||
input: "parallel:preprare",
|
||||
dictionary: %w[parallel:prepare parallel:create parallel:rake parallel:migrate],
|
||||
separator: ":"
|
||||
end
|
||||
|
||||
def test_special_words_mini
|
||||
tsp = DidYouMean::TreeSpellChecker.new(dictionary: MINI_DIRECTORIES)
|
||||
special_words_mini.each do |word, word_error|
|
||||
s = tsp.correct(word_error).first
|
||||
assert_match s, word
|
||||
[
|
||||
%w(test/fixtures/book.rb test/fixture/book.rb),
|
||||
%w(test/edit_distance/jaro_winkler_test.rb test/edit_distace/jaro_winkler_test.rb),
|
||||
%w(test/edit_distance/jaro_winkler_test.rb teste/dit_distane/jaro_winkler_test.rb),
|
||||
%w(test/fixtures/book.rb test/fixturWes/book.rb),
|
||||
%w(test/test_helper.rb tes!t/test_helper.rb),
|
||||
%w(test/fixtures/book.rb test/hfixtures/book.rb),
|
||||
%w(test/edit_distance/jaro_winkler_test.rb test/eidt_distance/jaro_winkler_test.@rb),
|
||||
%w(test/spell_checker_test.rb test/spell_checke@r_test.rb),
|
||||
%w(test/tree_spell_human_typo_test.rb testt/ree_spell_human_typo_test.rb),
|
||||
%w(test/edit_distance/jaro_winkler_test.rb test/edit_distance/jaro_winkler_tuest.rb),
|
||||
].each do |expected, user_input|
|
||||
assert_tree_spell expected, input: user_input, dictionary: MINI_DIRECTORIES
|
||||
end
|
||||
|
||||
[
|
||||
%w(test/spell_checking/variable_name_check_test.rb test/spell_checking/vriabl_ename_check_test.rb),
|
||||
%w(test/spell_checking/key_name_check_test.rb tesit/spell_checking/key_name_choeck_test.rb),
|
||||
].each do |expected, user_input|
|
||||
assert_equal expected, DidYouMean::TreeSpellChecker.new(dictionary: MINI_DIRECTORIES).correct(user_input)[0]
|
||||
end
|
||||
end
|
||||
|
||||
def test_special_words_rspec
|
||||
tsp = DidYouMean::TreeSpellChecker.new(dictionary: RSPEC_DIRECTORIES)
|
||||
special_words_rspec.each do |word, word_error|
|
||||
s = tsp.correct(word_error)
|
||||
assert_match s.first, word
|
||||
end
|
||||
end
|
||||
|
||||
def special_words_rspec
|
||||
[
|
||||
['spec/rspec/core/formatters/exception_presenter_spec.rb','spec/rspec/core/formatters/eception_presenter_spec.rb'],
|
||||
['spec/rspec/core/ordering_spec.rb', 'spec/spec/core/odrering_spec.rb'],
|
||||
['spec/rspec/core/metadata_spec.rb', 'spec/rspec/core/metadata_spe.crb'],
|
||||
['spec/support/mathn_integration_support.rb', 'spec/support/mathn_itegrtion_support.rb']
|
||||
]
|
||||
%w(spec/rspec/core/formatters/exception_presenter_spec.rb spec/rspec/core/formatters/eception_presenter_spec.rb),
|
||||
%w(spec/rspec/core/metadata_spec.rb spec/rspec/core/metadata_spe.crb),
|
||||
%w(spec/rspec/core/ordering_spec.rb spec/spec/core/odrering_spec.rb),
|
||||
%w(spec/support/mathn_integration_support.rb spec/support/mathn_itegrtion_support.rb),
|
||||
].each do |expected, user_input|
|
||||
assert_tree_spell expected, input: user_input, dictionary: RSPEC_DIRECTORIES
|
||||
end
|
||||
|
||||
def special_words_mini
|
||||
[
|
||||
['test/fixtures/book.rb', 'test/fixture/book.rb'],
|
||||
['test/fixtures/book.rb', 'test/fixture/book.rb'],
|
||||
['test/edit_distance/jaro_winkler_test.rb', 'test/edit_distace/jaro_winkler_test.rb'],
|
||||
['test/edit_distance/jaro_winkler_test.rb', 'teste/dit_distane/jaro_winkler_test.rb'],
|
||||
['test/fixtures/book.rb', 'test/fixturWes/book.rb'],
|
||||
['test/test_helper.rb', 'tes!t/test_helper.rb'],
|
||||
['test/fixtures/book.rb', 'test/hfixtures/book.rb'],
|
||||
['test/edit_distance/jaro_winkler_test.rb', 'test/eidt_distance/jaro_winkler_test.@rb'],
|
||||
['test/spell_checker_test.rb', 'test/spell_checke@r_test.rb'],
|
||||
['test/tree_spell_human_typo_test.rb', 'testt/ree_spell_human_typo_test.rb'],
|
||||
['test/spell_checking/variable_name_check_test.rb', 'test/spell_checking/vriabl_ename_check_test.rb'],
|
||||
['test/spell_checking/key_name_check_test.rb', 'tesit/spell_checking/key_name_choeck_test.rb'],
|
||||
['test/edit_distance/jaro_winkler_test.rb', 'test/edit_distance/jaro_winkler_tuest.rb']
|
||||
]
|
||||
end
|
||||
|
||||
def test_file_in_root
|
||||
word = 'test/spell_checker_test.rb'
|
||||
word_error = 'test/spell_checker_test.r'
|
||||
suggestions = DidYouMean::TreeSpellChecker.new(dictionary: MINI_DIRECTORIES).correct word_error
|
||||
assert_equal word, suggestions.first
|
||||
assert_tree_spell "test/spell_checker_test.rb", input: "test/spell_checker_test.r", dictionary: MINI_DIRECTORIES
|
||||
end
|
||||
|
||||
def test_no_plausible_states
|
||||
word_error = 'testspell_checker_test.rb'
|
||||
suggestions = DidYouMean::TreeSpellChecker.new(dictionary: MINI_DIRECTORIES).correct word_error
|
||||
assert_equal [], suggestions
|
||||
assert_tree_spell [], input: "testspell_checker_test.rb", dictionary: MINI_DIRECTORIES
|
||||
end
|
||||
|
||||
def test_no_plausible_states_with_augmentation
|
||||
word_error = 'testspell_checker_test.rb'
|
||||
suggestions = DidYouMean::TreeSpellChecker.new(dictionary: MINI_DIRECTORIES).correct word_error
|
||||
assert_equal [], suggestions
|
||||
suggestions = DidYouMean::TreeSpellChecker.new(dictionary: MINI_DIRECTORIES, augment: true).correct word_error
|
||||
assert_equal 'test/spell_checker_test.rb', suggestions.first
|
||||
assert_tree_spell [], input: "testspell_checker_test.rb", dictionary: MINI_DIRECTORIES
|
||||
|
||||
suggestions = DidYouMean::TreeSpellChecker.new(dictionary: MINI_DIRECTORIES, augment: true).correct("testspell_checker_test.rb")
|
||||
|
||||
assert_equal suggestions.first, "test/spell_checker_test.rb"
|
||||
end
|
||||
|
||||
def test_no_idea_with_augmentation
|
||||
word_error = 'test/spell_checking/key_name.rb'
|
||||
suggestions = DidYouMean::TreeSpellChecker.new(dictionary: MINI_DIRECTORIES).correct word_error
|
||||
assert_equal [], suggestions
|
||||
suggestions = DidYouMean::TreeSpellChecker.new(dictionary: MINI_DIRECTORIES, augment: true).correct word_error
|
||||
assert_equal 'test/spell_checking/key_name_check_test.rb', suggestions.first
|
||||
assert_tree_spell [], input: "test/spell_checking/key_name.rb", dictionary: MINI_DIRECTORIES
|
||||
|
||||
suggestions = DidYouMean::TreeSpellChecker.new(dictionary: MINI_DIRECTORIES, augment: true).correct("test/spell_checking/key_name.rb")
|
||||
|
||||
assert_equal suggestions.first, "test/spell_checking/key_name_check_test.rb"
|
||||
end
|
||||
|
||||
def test_works_out_suggestions
|
||||
exp = ['spec/models/concerns/vixen_spec.rb',
|
||||
'spec/models/concerns/vixenus_spec.rb']
|
||||
suggestions = @tsp.correct(@test_str)
|
||||
assert_equal suggestions.to_set, exp.to_set
|
||||
assert_tree_spell %w(spec/models/concerns/vixen_spec.rb spec/models/concerns/vixenus_spec.rb),
|
||||
input: "spek/modeks/confirns/viken_spec.rb",
|
||||
dictionary: %w(spec/models/concerns/vixen_spec.rb spec/models/concerns/vixenus_spec.rb)
|
||||
end
|
||||
|
||||
def test_works_when_input_is_correct
|
||||
correct_input = 'spec/models/concerns/vixenus_spec.rb'
|
||||
suggestions = @tsp.correct correct_input
|
||||
assert_equal suggestions.first, correct_input
|
||||
assert_tree_spell "spec/models/concerns/vixenus_spec.rb",
|
||||
input: "spec/models/concerns/vixenus_spec.rb",
|
||||
dictionary: @dictionary
|
||||
end
|
||||
|
||||
def test_find_out_leaves_in_a_path
|
||||
path = 'spec/modals/confirms'
|
||||
names = @tsp.send(:find_leaves, path)
|
||||
assert_equal names.to_set, %w(abcd_spec.rb efgh_spec.rb).to_set
|
||||
names = @tree_spell_checker.find_leaves("spec/modals/confirms")
|
||||
|
||||
assert_equal %w[abcd_spec.rb efgh_spec.rb], names
|
||||
end
|
||||
|
||||
def test_works_out_nodes
|
||||
exp_paths = ['spec/models/concerns',
|
||||
'spec/models/confirms',
|
||||
'spec/modals/concerns',
|
||||
'spec/modals/confirms',
|
||||
'spec/controllers/concerns',
|
||||
'spec/controllers/confirms'].to_set
|
||||
states = @tsp.send(:parse_dimensions)
|
||||
exp_paths = ["spec/models/concerns",
|
||||
"spec/models/confirms",
|
||||
"spec/modals/concerns",
|
||||
"spec/modals/confirms",
|
||||
"spec/controllers/concerns",
|
||||
"spec/controllers/confirms"]
|
||||
|
||||
states = @tree_spell_checker.dimensions
|
||||
nodes = states[0].product(*states[1..-1])
|
||||
paths = @tsp.send(:possible_paths, nodes)
|
||||
assert_equal paths.to_set, exp_paths.to_set
|
||||
paths = @tree_spell_checker.possible_paths(nodes)
|
||||
|
||||
assert_equal paths, exp_paths
|
||||
end
|
||||
|
||||
def test_works_out_state_space
|
||||
suggestions = @tsp.send(:plausible_dimensions, @test_str)
|
||||
assert_equal suggestions, [["spec"], ["models", "modals"], ["confirms", "concerns"]]
|
||||
suggestions = @tree_spell_checker.plausible_dimensions(@test_str)
|
||||
|
||||
assert_equal [["spec"], %w[models modals], %w[confirms concerns]], suggestions
|
||||
end
|
||||
|
||||
def test_parses_dictionary
|
||||
states = @tsp.send(:parse_dimensions)
|
||||
assert_equal states, [["spec"], ["models", "modals", "controllers"], ["concerns", "confirms"]]
|
||||
states = @tree_spell_checker.dimensions
|
||||
|
||||
assert_equal [["spec"], %w[models modals controllers], %w[concerns confirms]], states
|
||||
end
|
||||
|
||||
def test_parses_elementary_dictionary
|
||||
dictionary = ['spec/models/user_spec.rb', 'spec/services/account_spec.rb']
|
||||
tsp = DidYouMean::TreeSpellChecker.new(dictionary: dictionary)
|
||||
states = tsp.send(:parse_dimensions)
|
||||
assert_equal states, [['spec'], ['models', 'services']]
|
||||
dimensions = DidYouMean::TreeSpellChecker
|
||||
.new(dictionary: %w(spec/models/user_spec.rb spec/services/account_spec.rb))
|
||||
.dimensions
|
||||
|
||||
assert_equal [["spec"], %w[models services]], dimensions
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def assert_tree_spell(expected, input:, dictionary:, separator: "/")
|
||||
suggestions = DidYouMean::TreeSpellChecker.new(dictionary: dictionary, separator: separator).correct(input)
|
||||
|
||||
assert_equal Array(expected), suggestions, "Expected to suggest #{expected}, but got #{suggestions.inspect}"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,6 +3,7 @@ require_relative './helper'
|
|||
class VerboseFormatterTest < Test::Unit::TestCase
|
||||
def setup
|
||||
require_relative File.join(DidYouMean::TestHelper.root, 'verbose')
|
||||
|
||||
DidYouMean.formatter = DidYouMean::VerboseFormatter.new
|
||||
end
|
||||
|
||||
|
|
|
@ -4,6 +4,9 @@ module TreeSpell
|
|||
# Simulate an error prone human typist
|
||||
# see doc/human_typo_api.md for the api description
|
||||
class HumanTypo
|
||||
POPULAR_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?<>,.!`+=-_":;@#$%^&*()'.split("").freeze
|
||||
ACTION_TYPES = %i(insert transpose delete substitute).freeze
|
||||
|
||||
def initialize(input, lambda: 0.05)
|
||||
@input = input
|
||||
check_input
|
||||
|
@ -15,7 +18,7 @@ module TreeSpell
|
|||
@word = input.dup
|
||||
i_place = initialize_i_place
|
||||
loop do
|
||||
action = action_type
|
||||
action = ACTION_TYPES.sample
|
||||
@word = make_change action, i_place
|
||||
@len = word.length
|
||||
i_place += exponential
|
||||
|
@ -41,40 +44,17 @@ module TreeSpell
|
|||
(rand / (lambda / 2)).to_i
|
||||
end
|
||||
|
||||
def rand_char
|
||||
popular_chars = alphabetic_characters + special_characters
|
||||
n = popular_chars.length
|
||||
popular_chars[rand(n)]
|
||||
end
|
||||
|
||||
def alphabetic_characters
|
||||
('a'..'z').to_a.join + ('A'..'Z').to_a.join
|
||||
end
|
||||
|
||||
def special_characters
|
||||
'?<>,.!`+=-_":;@#$%^&*()'
|
||||
end
|
||||
|
||||
def toss
|
||||
return +1 if rand >= 0.5
|
||||
-1
|
||||
end
|
||||
|
||||
def action_type
|
||||
[:insert, :transpose, :delete, :substitute][rand(4)]
|
||||
end
|
||||
|
||||
def make_change(action, i_place)
|
||||
cw = ChangeWord.new(word)
|
||||
case action
|
||||
when :delete
|
||||
cw.deletion(i_place)
|
||||
when :insert
|
||||
cw.insertion(i_place, rand_char)
|
||||
cw.insertion(i_place, POPULAR_CHARS.sample)
|
||||
when :substitute
|
||||
cw.substitution(i_place, rand_char)
|
||||
cw.substitution(i_place, POPULAR_CHARS.sample)
|
||||
when :transpose
|
||||
cw.transposition(i_place, toss)
|
||||
cw.transposition(i_place, rand >= 0.5 ? +1 : -1)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче