did_you_mean 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +9 -18
- data/CHANGELOG.md +37 -3
- data/README.md +8 -0
- data/Rakefile +13 -10
- data/benchmark/require_path_checker.rb +47 -0
- data/lib/did_you_mean.rb +3 -1
- data/lib/did_you_mean/spell_checkers/method_name_checker.rb +6 -1
- data/lib/did_you_mean/spell_checkers/require_path_checker.rb +35 -0
- data/lib/did_you_mean/tree_spell_checker.rb +61 -89
- data/lib/did_you_mean/version.rb +1 -1
- data/test/fixtures/book.rb +1 -1
- data/test/spell_checking/test_class_name_check.rb +4 -2
- data/test/spell_checking/test_method_name_check.rb +7 -0
- data/test/spell_checking/test_require_path_check.rb +32 -0
- data/test/test_tree_spell_checker.rb +100 -103
- data/test/test_verbose_formatter.rb +2 -0
- data/test/tree_spell/human_typo.rb +7 -27
- metadata +7 -5
- data/lib/did_you_mean/experimental/initializer_name_correction.rb +0 -20
- data/lib/did_you_mean/experimental/ivar_name_correction.rb +0 -76
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3befc8f677ec57e2975d04b2095fcecf7314ff207150aa994b197894dba883af
|
|
4
|
+
data.tar.gz: dc5a44ad2f6058ef68913f4080a23141f39b23f3b30129fe753d5d88cca85cc0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4d60d78614d4a6650b2be87599d3c3d867ab74f4cdf7f6083594229e3c51588af57a5c9574ca35ced229558f4280cd3677cbd7944cc5cef55e34a4f7ff7addf3
|
|
7
|
+
data.tar.gz: df5f07a5893afee4e03af416230ee828c7c2c2dfb8e5e35b5c525bebdff03710645b4af192563dc546abd4c210f8f2e8ce15e90124503488b74db19bd700f71c
|
data/.github/workflows/ruby.yml
CHANGED
|
@@ -13,36 +13,27 @@ jobs:
|
|
|
13
13
|
runs-on: ubuntu-latest
|
|
14
14
|
strategy:
|
|
15
15
|
matrix:
|
|
16
|
-
ruby: [ '2.5.
|
|
16
|
+
ruby: [ '2.5.8', '2.6.6', '2.7.1', 'ruby-head', 'jruby-9.2.11.1', 'jruby-head' ]
|
|
17
17
|
steps:
|
|
18
18
|
- uses: actions/checkout@v1
|
|
19
|
-
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
- name: Set up Ruby
|
|
23
|
-
run: |
|
|
24
|
-
source $HOME/.rvm/scripts/rvm
|
|
25
|
-
rvm install ${{ matrix.ruby }} --binary
|
|
26
|
-
rvm --default use ${{ matrix.ruby }}
|
|
19
|
+
- uses: ruby/setup-ruby@v1
|
|
20
|
+
with:
|
|
21
|
+
ruby-version: ${{ matrix.ruby }}
|
|
27
22
|
- name: Build and test with Rake
|
|
28
23
|
run: |
|
|
29
|
-
source $HOME/.rvm/scripts/rvm
|
|
30
24
|
gem i test-unit
|
|
31
|
-
rake
|
|
25
|
+
RUBYOPT='--disable-did_you_mean' rake
|
|
32
26
|
|
|
33
27
|
benchmark:
|
|
34
28
|
runs-on: ubuntu-latest
|
|
35
29
|
steps:
|
|
36
30
|
- uses: actions/checkout@v1
|
|
37
|
-
-
|
|
38
|
-
uses: actions/setup-ruby@v1
|
|
31
|
+
- uses: ruby/setup-ruby@v1
|
|
39
32
|
with:
|
|
40
|
-
ruby-version: 2.
|
|
33
|
+
ruby-version: 2.7.1
|
|
41
34
|
- name: Test performance and accuracy
|
|
42
35
|
run: |
|
|
43
36
|
gem install bundler
|
|
44
37
|
bundle install --jobs 4 --retry 2
|
|
45
|
-
|
|
46
|
-
bundle exec rake
|
|
47
|
-
bundle exec rake test:explore
|
|
48
|
-
bundle exec rake benchmark:memory
|
|
38
|
+
RUBYOPT='--disable-did_you_mean' bundle exec rake test:accuracy
|
|
39
|
+
RUBYOPT='--disable-did_you_mean' bundle exec rake benchmark:memory
|
data/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,41 @@
|
|
|
1
|
-
## v1.
|
|
1
|
+
## v1.5.0
|
|
2
2
|
|
|
3
|
-
-
|
|
4
|
-
|
|
3
|
+
- Suggest require paths on LoadError ([#143](https://github.com/ruby/did_you_mean/pull/143))
|
|
4
|
+
|
|
5
|
+
## [v1.4.0](https://github.com/ruby/did_you_mean/tree/v1.4.0)
|
|
6
|
+
|
|
7
|
+
_<sup>released at 2020-05-09 02:56:43 UTC</sup>_
|
|
8
|
+
|
|
9
|
+
As of Ruby 2.7, the `did_you_mean` gem has been promoted up to a default gem. I would like to thank [@kddeisz](https://github.com/kddeisz) for his hard work on making the entire gem easily portable the main ruby/ruby repo ([#132](https://github.com/ruby/did_you_mean/pull/132), [#131](https://github.com/ruby/did_you_mean/pull/131), and [ruby/ruby#2631](https://github.com/ruby/ruby/pull/2631)).
|
|
10
|
+
|
|
11
|
+
#### Features
|
|
12
|
+
|
|
13
|
+
- Add a new tree spell checker ([#119](https://github.com/ruby/did_you_mean/pull/119), [@obromios](https://github.com/obromios))
|
|
14
|
+
- Add a public API for registering an error ([#123](https://github.com/ruby/did_you_mean/pull/123), [@kddeisz](https://github.com/kddeisz))
|
|
15
|
+
|
|
16
|
+
#### Bug fixes
|
|
17
|
+
|
|
18
|
+
- Fixes a bug where wrong suggestion could be made when string requested on hash and keys are symbols ([@localhostdotdev](https://github.com/localhostdotdev), [#134](https://github.com/ruby/did_you_mean/pull/134))
|
|
19
|
+
|
|
20
|
+
#### Breaking changes
|
|
21
|
+
|
|
22
|
+
- Experimental features have been removed ([#135](https://github.com/ruby/did_you_mean/pull/135))
|
|
23
|
+
|
|
24
|
+
#### Internal changes
|
|
25
|
+
|
|
26
|
+
- Replace Travis CI with GitHub Actions ([#124](https://github.com/ruby/did_you_mean/pull/124))
|
|
27
|
+
- Drop mintiest dependency ([#129](https://github.com/ruby/did_you_mean/pull/129))
|
|
28
|
+
- Drop delegate dependency ([#138](https://github.com/ruby/did_you_mean/pull/138))
|
|
29
|
+
|
|
30
|
+
## [v1.3.1](https://github.com/ruby/did_you_mean/tree/v1.3.1)
|
|
31
|
+
|
|
32
|
+
_<sup>released at 2019-09-29 03:58:46 UTC</sup>_
|
|
33
|
+
|
|
34
|
+
#### Bug fixes
|
|
35
|
+
|
|
36
|
+
- Fixes a test failure in Ruby core where DYM attempts to mutate immutable strings from `Symbol#to_s` ([#125](https://github.com/ruby/did_you_mean/pull/125), [@nobu](https://github.com/nobu), [@eregon](https://github.com/eregon), [@MSP-Greg](https://github.com/MSP-Greg))
|
|
37
|
+
- Removes the empty `tmp/` directory to comply with `rpmlint` ([#122](https://github.com/ruby/did_you_mean/issues/122), [@pvalena](https://github.com/pvalena))
|
|
38
|
+
- Fixes a bug where suggestions are not shown on subsequent errors ([#120](https://github.com/ruby/did_you_mean/issues/120), [@localhostdotdev](https://github.com/localhostdotdev))
|
|
5
39
|
|
|
6
40
|
## [v1.3.0](https://github.com/ruby/did_you_mean/tree/v1.3.0)
|
|
7
41
|
|
data/README.md
CHANGED
|
@@ -61,6 +61,14 @@ hash.fetch(:fooo)
|
|
|
61
61
|
# Did you mean? :foo
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
+
### LoadError
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
require 'net-http'
|
|
68
|
+
# => LoadError (cannot load such file -- net-http)
|
|
69
|
+
# Did you mean? net/http
|
|
70
|
+
```
|
|
71
|
+
|
|
64
72
|
## Verbose Formatter
|
|
65
73
|
|
|
66
74
|
This verbose formatter changes the error message format to take more lines/spaces so it'll be slightly easier to read the suggestions. This formatter can totally be used in any environment including production.
|
data/Rakefile
CHANGED
|
@@ -7,13 +7,16 @@ Rake::TestTask.new do |task|
|
|
|
7
7
|
task.test_files = Dir['test/**/test_*.rb'].reject {|path| path.end_with?("test_explore.rb") }
|
|
8
8
|
task.verbose = true
|
|
9
9
|
task.warning = true
|
|
10
|
+
task.ruby_opts = %w[ --disable-did_you_mean ]
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
Rake::TestTask.new("test:explore") do |task|
|
|
13
14
|
task.libs << "test"
|
|
14
|
-
|
|
15
|
-
task.
|
|
16
|
-
task.
|
|
15
|
+
|
|
16
|
+
task.pattern = 'test/tree_spell/test_explore.rb'
|
|
17
|
+
task.verbose = true
|
|
18
|
+
task.warning = true
|
|
19
|
+
task.ruby_opts = %w[ --disable-did_you_mean ]
|
|
17
20
|
end
|
|
18
21
|
|
|
19
22
|
task default: %i(test)
|
|
@@ -22,7 +25,7 @@ namespace :test do
|
|
|
22
25
|
namespace :accuracy do
|
|
23
26
|
desc "Download Wiktionary's Simple English data and save it as a dictionary"
|
|
24
27
|
task :prepare do
|
|
25
|
-
sh 'ruby evaluation/dictionary_generator.rb
|
|
28
|
+
sh "RUBYOPT='--disable-did_you_mean' ruby evaluation/dictionary_generator.rb"
|
|
26
29
|
end
|
|
27
30
|
end
|
|
28
31
|
|
|
@@ -34,7 +37,7 @@ namespace :test do
|
|
|
34
37
|
puts "\n"
|
|
35
38
|
end
|
|
36
39
|
|
|
37
|
-
sh 'ruby evaluation/calculator.rb
|
|
40
|
+
sh "RUBYOPT='--disable-did_you_mean' ruby evaluation/calculator.rb"
|
|
38
41
|
end
|
|
39
42
|
end
|
|
40
43
|
|
|
@@ -42,29 +45,29 @@ namespace :benchmark do
|
|
|
42
45
|
namespace :ips do
|
|
43
46
|
desc "Measure performance of the gem's Jaro distance implementation"
|
|
44
47
|
task :jaro do
|
|
45
|
-
sh "ruby benchmark/jaro_winkler/speed.rb"
|
|
48
|
+
sh "RUBYOPT='--disable-did_you_mean' ruby benchmark/jaro_winkler/speed.rb"
|
|
46
49
|
end
|
|
47
50
|
|
|
48
51
|
desc "Benchmark performance of the gem's Levenshtein distance implementation"
|
|
49
52
|
task :levenshtein do
|
|
50
|
-
sh "ruby benchmark/levenshtein/speed.rb"
|
|
53
|
+
sh "RUBYOPT='--disable-did_you_mean' ruby benchmark/levenshtein/speed.rb"
|
|
51
54
|
end
|
|
52
55
|
end
|
|
53
56
|
|
|
54
57
|
desc "Benchmark memory usage in the gem's spell checker"
|
|
55
58
|
task :memory do
|
|
56
|
-
sh "ruby benchmark/memory_usage.rb"
|
|
59
|
+
sh "RUBYOPT='--disable-did_you_mean' ruby benchmark/memory_usage.rb"
|
|
57
60
|
end
|
|
58
61
|
|
|
59
62
|
namespace :memory do
|
|
60
63
|
desc "Benchmark memory usage in the gem's Jaro distance implementation"
|
|
61
64
|
task :jaro do
|
|
62
|
-
sh "ruby benchmark/jaro_winkler/memory_usage.rb"
|
|
65
|
+
sh "RUBYOPT='--disable-did_you_mean' ruby benchmark/jaro_winkler/memory_usage.rb"
|
|
63
66
|
end
|
|
64
67
|
|
|
65
68
|
desc "Benchmark memory usage in the gem's Levenshtein distance implementation"
|
|
66
69
|
task :levenshtein do
|
|
67
|
-
sh "ruby benchmark/levenshtein/memory_usage.rb"
|
|
70
|
+
sh "RUBYOPT='--disable-did_you_mean' ruby benchmark/levenshtein/memory_usage.rb"
|
|
68
71
|
end
|
|
69
72
|
end
|
|
70
73
|
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
#
|
|
3
|
+
# Run the following command to run this script:
|
|
4
|
+
#
|
|
5
|
+
# $ ruby --disable-did_you_mean benchmark/require_path_checker.rb
|
|
6
|
+
#
|
|
7
|
+
|
|
8
|
+
require_relative '../lib/did_you_mean/spell_checkers/require_path_checker'
|
|
9
|
+
|
|
10
|
+
require 'benchmark/ips'
|
|
11
|
+
|
|
12
|
+
Benchmark.ips do |x|
|
|
13
|
+
x.config(time: 10, warmup: 10)
|
|
14
|
+
|
|
15
|
+
exception_with_slash = begin
|
|
16
|
+
require 'net/htto'
|
|
17
|
+
rescue LoadError => error
|
|
18
|
+
error
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
exception_without_slash = begin
|
|
22
|
+
require 'net-http'
|
|
23
|
+
rescue LoadError => error
|
|
24
|
+
error
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
checker_for_path = DidYouMean::RequirePathChecker.new(exception_with_slash)
|
|
28
|
+
checker_for_file = DidYouMean::RequirePathChecker.new(exception_without_slash)
|
|
29
|
+
|
|
30
|
+
x.report "original (with a /)" do
|
|
31
|
+
checker_for_path.corrections
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
x.report "original (without /)" do
|
|
35
|
+
checker_for_file.corrections
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
#x.report "proposed (with a /)" do
|
|
39
|
+
# checker_for_path.experiment
|
|
40
|
+
#end
|
|
41
|
+
#
|
|
42
|
+
#x.report "proposed (without /)" do
|
|
43
|
+
# checker_for_file.experiment
|
|
44
|
+
#end
|
|
45
|
+
|
|
46
|
+
x.compare!
|
|
47
|
+
end
|
data/lib/did_you_mean.rb
CHANGED
|
@@ -6,6 +6,7 @@ require_relative 'did_you_mean/spell_checkers/name_error_checkers'
|
|
|
6
6
|
require_relative 'did_you_mean/spell_checkers/method_name_checker'
|
|
7
7
|
require_relative 'did_you_mean/spell_checkers/key_error_checker'
|
|
8
8
|
require_relative 'did_you_mean/spell_checkers/null_checker'
|
|
9
|
+
require_relative 'did_you_mean/spell_checkers/require_path_checker'
|
|
9
10
|
require_relative 'did_you_mean/formatters/plain_formatter'
|
|
10
11
|
require_relative 'did_you_mean/tree_spell_checker'
|
|
11
12
|
|
|
@@ -95,8 +96,9 @@ module DidYouMean
|
|
|
95
96
|
correct_error NameError, NameErrorCheckers
|
|
96
97
|
correct_error KeyError, KeyErrorChecker
|
|
97
98
|
correct_error NoMethodError, MethodNameChecker
|
|
99
|
+
correct_error LoadError, RequirePathChecker if RUBY_VERSION >= '2.8.0'
|
|
98
100
|
|
|
99
|
-
# Returns the
|
|
101
|
+
# Returns the currently set formatter. By default, it is set to +DidYouMean::Formatter+.
|
|
100
102
|
def self.formatter
|
|
101
103
|
@@formatter
|
|
102
104
|
end
|
|
@@ -43,7 +43,12 @@ module DidYouMean
|
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def corrections
|
|
46
|
-
@corrections ||=
|
|
46
|
+
@corrections ||= begin
|
|
47
|
+
dictionary = method_names
|
|
48
|
+
dictionary = RB_RESERVED_WORDS + dictionary if @private_call
|
|
49
|
+
|
|
50
|
+
SpellChecker.new(dictionary: dictionary).correct(method_name) - names_to_exclude
|
|
51
|
+
end
|
|
47
52
|
end
|
|
48
53
|
|
|
49
54
|
def method_names
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../spell_checker"
|
|
4
|
+
require_relative "../tree_spell_checker"
|
|
5
|
+
|
|
6
|
+
module DidYouMean
|
|
7
|
+
class RequirePathChecker
|
|
8
|
+
attr_reader :path
|
|
9
|
+
|
|
10
|
+
INITIAL_LOAD_PATH = $LOAD_PATH.dup.freeze
|
|
11
|
+
ENV_SPECIFIC_EXT = ".#{RbConfig::CONFIG["DLEXT"]}"
|
|
12
|
+
|
|
13
|
+
private_constant :INITIAL_LOAD_PATH, :ENV_SPECIFIC_EXT
|
|
14
|
+
|
|
15
|
+
def self.requireables
|
|
16
|
+
@requireables ||= INITIAL_LOAD_PATH
|
|
17
|
+
.flat_map {|path| Dir.glob("**/???*{.rb,#{ENV_SPECIFIC_EXT}}", base: path) }
|
|
18
|
+
.map {|path| path.chomp!(".rb") || path.chomp!(ENV_SPECIFIC_EXT) }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def initialize(exception)
|
|
22
|
+
@path = exception.path
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def corrections
|
|
26
|
+
@corrections ||= begin
|
|
27
|
+
threshold = path.size * 2
|
|
28
|
+
dictionary = self.class.requireables.reject {|str| str.size >= threshold }
|
|
29
|
+
spell_checker = path.include?("/") ? TreeSpellChecker : SpellChecker
|
|
30
|
+
|
|
31
|
+
spell_checker.new(dictionary: dictionary).correct(path).uniq
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -1,137 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module DidYouMean
|
|
2
4
|
# spell checker for a dictionary that has a tree
|
|
3
5
|
# structure, see doc/tree_spell_checker_api.md
|
|
4
6
|
class TreeSpellChecker
|
|
5
|
-
attr_reader :dictionary, :
|
|
7
|
+
attr_reader :dictionary, :separator, :augment
|
|
6
8
|
|
|
7
9
|
def initialize(dictionary:, separator: '/', augment: nil)
|
|
8
10
|
@dictionary = dictionary
|
|
9
11
|
@separator = separator
|
|
10
12
|
@augment = augment
|
|
11
|
-
@dimensions = parse_dimensions
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
def correct(input)
|
|
15
|
-
plausibles = plausible_dimensions
|
|
16
|
-
return
|
|
17
|
-
suggestions = find_suggestions input, plausibles
|
|
18
|
-
return no_idea(input) if suggestions.empty?
|
|
19
|
-
suggestions
|
|
20
|
-
end
|
|
16
|
+
plausibles = plausible_dimensions(input)
|
|
17
|
+
return fall_back_to_normal_spell_check(input) if plausibles.empty?
|
|
21
18
|
|
|
22
|
-
|
|
19
|
+
suggestions = find_suggestions(input, plausibles)
|
|
20
|
+
return fall_back_to_normal_spell_check(input) if suggestions.empty?
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
ParseDimensions.new(dictionary, separator).call
|
|
22
|
+
suggestions
|
|
26
23
|
end
|
|
27
24
|
|
|
28
|
-
def
|
|
29
|
-
|
|
30
|
-
paths = possible_paths states
|
|
31
|
-
leaf = input.split(separator).last
|
|
32
|
-
ideas = find_ideas(paths, leaf)
|
|
33
|
-
ideas.compact.flatten
|
|
25
|
+
def dictionary_without_leaves
|
|
26
|
+
@dictionary_without_leaves ||= dictionary.map { |word| word.split(separator)[0..-2] }.uniq
|
|
34
27
|
end
|
|
35
28
|
|
|
36
|
-
def
|
|
37
|
-
|
|
38
|
-
::DidYouMean::SpellChecker.new(dictionary: dictionary).correct(input)
|
|
29
|
+
def tree_depth
|
|
30
|
+
@tree_depth ||= dictionary_without_leaves.max { |a, b| a.size <=> b.size }.size
|
|
39
31
|
end
|
|
40
32
|
|
|
41
|
-
def
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
ideas_to_paths ideas, leaf, names, path
|
|
46
|
-
end
|
|
33
|
+
def dimensions
|
|
34
|
+
@dimensions ||= tree_depth.times.map do |index|
|
|
35
|
+
dictionary_without_leaves.map { |element| element[index] }.compact.uniq
|
|
36
|
+
end
|
|
47
37
|
end
|
|
48
38
|
|
|
49
|
-
def
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
39
|
+
def find_leaves(path)
|
|
40
|
+
path_with_separator = "#{path}#{separator}"
|
|
41
|
+
|
|
42
|
+
dictionary
|
|
43
|
+
.select {|str| str.include?(path_with_separator) }
|
|
44
|
+
.map {|str| str.gsub(path_with_separator, '') }
|
|
53
45
|
end
|
|
54
46
|
|
|
55
|
-
def
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
47
|
+
def plausible_dimensions(input)
|
|
48
|
+
input.split(separator)[0..-2]
|
|
49
|
+
.map
|
|
50
|
+
.with_index { |element, index| correct_element(dimensions[index], element) if dimensions[index] }
|
|
51
|
+
.compact
|
|
60
52
|
end
|
|
61
53
|
|
|
62
54
|
def possible_paths(states)
|
|
63
|
-
states.map
|
|
64
|
-
state.join separator
|
|
65
|
-
end
|
|
55
|
+
states.map { |state| state.join(separator) }
|
|
66
56
|
end
|
|
67
57
|
|
|
68
|
-
|
|
69
|
-
elements = input.split(separator)[0..-2]
|
|
70
|
-
elements.each_with_index.map do |element, i|
|
|
71
|
-
next if dimensions[i].nil?
|
|
72
|
-
CorrectElement.new.call dimensions[i], element
|
|
73
|
-
end.compact
|
|
74
|
-
end
|
|
75
|
-
end
|
|
58
|
+
private
|
|
76
59
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
60
|
+
def find_suggestions(input, plausibles)
|
|
61
|
+
states = plausibles[0].product(*plausibles[1..-1])
|
|
62
|
+
paths = possible_paths(states)
|
|
63
|
+
leaf = input.split(separator).last
|
|
64
|
+
|
|
65
|
+
find_ideas(paths, leaf)
|
|
82
66
|
end
|
|
83
67
|
|
|
84
|
-
def
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
elements.to_set.to_a
|
|
89
|
-
end
|
|
68
|
+
def fall_back_to_normal_spell_check(input)
|
|
69
|
+
return [] unless augment
|
|
70
|
+
|
|
71
|
+
::DidYouMean::SpellChecker.new(dictionary: dictionary).correct(input)
|
|
90
72
|
end
|
|
91
73
|
|
|
92
|
-
|
|
74
|
+
def find_ideas(paths, leaf)
|
|
75
|
+
paths.flat_map do |path|
|
|
76
|
+
names = find_leaves(path)
|
|
77
|
+
ideas = correct_element(names, leaf)
|
|
93
78
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
elements = a.split(separator)
|
|
97
|
-
elements[0..-2]
|
|
98
|
-
end.to_set.to_a
|
|
79
|
+
ideas_to_paths(ideas, leaf, names, path)
|
|
80
|
+
end.compact
|
|
99
81
|
end
|
|
100
82
|
|
|
101
|
-
def
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
83
|
+
def ideas_to_paths(ideas, leaf, names, path)
|
|
84
|
+
if ideas.empty?
|
|
85
|
+
nil
|
|
86
|
+
elsif names.include?(leaf)
|
|
87
|
+
["#{path}#{separator}#{leaf}"]
|
|
88
|
+
else
|
|
89
|
+
ideas.map {|str| "#{path}#{separator}#{str}" }
|
|
108
90
|
end
|
|
109
|
-
dimensions
|
|
110
91
|
end
|
|
111
92
|
|
|
112
|
-
|
|
113
|
-
|
|
93
|
+
def correct_element(names, element)
|
|
94
|
+
return names if names.size == 1
|
|
114
95
|
|
|
115
|
-
|
|
116
|
-
class CorrectElement
|
|
117
|
-
def initialize
|
|
118
|
-
end
|
|
96
|
+
str = normalize(element)
|
|
119
97
|
|
|
120
|
-
|
|
121
|
-
return names if names.size == 1
|
|
122
|
-
str = normalize element
|
|
123
|
-
return [str] if names.include? str
|
|
124
|
-
checker = ::DidYouMean::SpellChecker.new(dictionary: names)
|
|
125
|
-
checker.correct(str)
|
|
126
|
-
end
|
|
98
|
+
return [str] if names.include?(str)
|
|
127
99
|
|
|
128
|
-
|
|
100
|
+
::DidYouMean::SpellChecker.new(dictionary: names).correct(str)
|
|
101
|
+
end
|
|
129
102
|
|
|
130
|
-
def normalize(
|
|
131
|
-
str = leaf.dup
|
|
103
|
+
def normalize(str)
|
|
132
104
|
str.downcase!
|
|
133
|
-
|
|
134
|
-
str
|
|
105
|
+
str.tr!('@', ' ') if str.include?('@')
|
|
106
|
+
str
|
|
135
107
|
end
|
|
136
108
|
end
|
|
137
109
|
end
|
data/lib/did_you_mean/version.rb
CHANGED
data/test/fixtures/book.rb
CHANGED
|
@@ -66,11 +66,13 @@ class ClassNameCheckTest < Test::Unit::TestCase
|
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
def test_does_not_suggest_user_input
|
|
69
|
-
|
|
69
|
+
Book.send(:remove_const, :Spine) if Book.constants.include?(:Spine)
|
|
70
|
+
|
|
71
|
+
error = assert_raise(NameError) { ::Book::Spine }
|
|
70
72
|
|
|
71
73
|
# This is a weird require, but in a multi-threaded condition, a constant may
|
|
72
74
|
# be loaded between when a NameError occurred and when the spell checker
|
|
73
|
-
#
|
|
75
|
+
# attempts to find a possible suggestion. The manual require here simulates
|
|
74
76
|
# a race condition a single test.
|
|
75
77
|
require_relative '../fixtures/book'
|
|
76
78
|
|
|
@@ -137,4 +137,11 @@ class MethodNameCheckTest < Test::Unit::TestCase
|
|
|
137
137
|
assert_correction :yield, error.corrections
|
|
138
138
|
assert_match "Did you mean? yield", error.to_s
|
|
139
139
|
end
|
|
140
|
+
|
|
141
|
+
def test_does_not_suggest_yield
|
|
142
|
+
error = assert_raise(NoMethodError) { 1.yeild }
|
|
143
|
+
|
|
144
|
+
assert_correction [], error.corrections
|
|
145
|
+
assert_not_match(/Did you mean\? +yield/, error.to_s)
|
|
146
|
+
end if RUBY_ENGINE != "jruby"
|
|
140
147
|
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require_relative '../helper'
|
|
2
|
+
|
|
3
|
+
return if !(RUBY_VERSION >= '2.8.0')
|
|
4
|
+
|
|
5
|
+
class RequirePathCheckTest < Test::Unit::TestCase
|
|
6
|
+
include DidYouMean::TestHelper
|
|
7
|
+
|
|
8
|
+
def test_load_error_from_require_has_suggestions
|
|
9
|
+
error = assert_raise LoadError do
|
|
10
|
+
require 'open_struct'
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
assert_correction 'ostruct', error.corrections
|
|
14
|
+
assert_match "Did you mean? ostruct", error.to_s
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_load_error_from_require_for_nested_files_has_suggestions
|
|
18
|
+
error = assert_raise LoadError do
|
|
19
|
+
require 'net/htt'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
assert_correction 'net/http', error.corrections
|
|
23
|
+
assert_match "Did you mean? net/http", error.to_s
|
|
24
|
+
|
|
25
|
+
error = assert_raise LoadError do
|
|
26
|
+
require 'net-http'
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
assert_correction ['net/http', 'net/https'], error.corrections
|
|
30
|
+
assert_match "Did you mean? net/http", error.to_s
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
require 'yaml'
|
|
1
|
+
# frozen_string_literal: true
|
|
3
2
|
|
|
4
|
-
|
|
3
|
+
require "yaml"
|
|
4
|
+
|
|
5
|
+
require_relative "./helper"
|
|
5
6
|
|
|
6
7
|
class TreeSpellCheckerTest < Test::Unit::TestCase
|
|
7
|
-
MINI_DIRECTORIES = YAML.load_file(File.expand_path(
|
|
8
|
-
RSPEC_DIRECTORIES = YAML.load_file(File.expand_path(
|
|
8
|
+
MINI_DIRECTORIES = YAML.load_file(File.expand_path("fixtures/mini_dir.yml", __dir__))
|
|
9
|
+
RSPEC_DIRECTORIES = YAML.load_file(File.expand_path("fixtures/rspec_dir.yml", __dir__))
|
|
9
10
|
|
|
10
11
|
def setup
|
|
11
12
|
@dictionary =
|
|
@@ -20,154 +21,150 @@ class TreeSpellCheckerTest < Test::Unit::TestCase
|
|
|
20
21
|
spec/models/gfsga_spec.rb
|
|
21
22
|
spec/controllers/vixen_controller_spec.rb
|
|
22
23
|
)
|
|
23
|
-
@test_str =
|
|
24
|
-
@
|
|
24
|
+
@test_str = "spek/modeks/confirns/viken_spec.rb"
|
|
25
|
+
@tree_spell_checker = DidYouMean::TreeSpellChecker.new(dictionary: @dictionary)
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
def test_corrupt_root
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
s = tsp.correct(word_error).first
|
|
32
|
-
assert_match s, word
|
|
29
|
+
assert_tree_spell "test/verbose_formatter_test.rb",
|
|
30
|
+
input: "btets/cverbose_formatter_etst.rb suggestions",
|
|
31
|
+
dictionary: MINI_DIRECTORIES
|
|
33
32
|
end
|
|
34
33
|
|
|
35
34
|
def test_leafless_state
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
assert_tree_spell "spec/modals/confirms/efgh_spec.rb",
|
|
36
|
+
input: "spec/modals/confirXX/efgh_spec.rb",
|
|
37
|
+
dictionary: [*@dictionary, "spec/features"]
|
|
38
|
+
|
|
39
|
+
assert_tree_spell "spec/features",
|
|
40
|
+
input: "spec/featuresXX",
|
|
41
|
+
dictionary: [*@dictionary, "spec/features"]
|
|
43
42
|
end
|
|
44
43
|
|
|
45
44
|
def test_rake_dictionary
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
assert_match s, 'parallel:prepare'
|
|
45
|
+
assert_tree_spell "parallel:prepare",
|
|
46
|
+
input: "parallel:preprare",
|
|
47
|
+
dictionary: %w[parallel:prepare parallel:create parallel:rake parallel:migrate],
|
|
48
|
+
separator: ":"
|
|
51
49
|
end
|
|
52
50
|
|
|
53
51
|
def test_special_words_mini
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
[
|
|
53
|
+
%w(test/fixtures/book.rb test/fixture/book.rb),
|
|
54
|
+
%w(test/edit_distance/jaro_winkler_test.rb test/edit_distace/jaro_winkler_test.rb),
|
|
55
|
+
%w(test/edit_distance/jaro_winkler_test.rb teste/dit_distane/jaro_winkler_test.rb),
|
|
56
|
+
%w(test/fixtures/book.rb test/fixturWes/book.rb),
|
|
57
|
+
%w(test/test_helper.rb tes!t/test_helper.rb),
|
|
58
|
+
%w(test/fixtures/book.rb test/hfixtures/book.rb),
|
|
59
|
+
%w(test/edit_distance/jaro_winkler_test.rb test/eidt_distance/jaro_winkler_test.@rb),
|
|
60
|
+
%w(test/spell_checker_test.rb test/spell_checke@r_test.rb),
|
|
61
|
+
%w(test/tree_spell_human_typo_test.rb testt/ree_spell_human_typo_test.rb),
|
|
62
|
+
%w(test/edit_distance/jaro_winkler_test.rb test/edit_distance/jaro_winkler_tuest.rb),
|
|
63
|
+
].each do |expected, user_input|
|
|
64
|
+
assert_tree_spell expected, input: user_input, dictionary: MINI_DIRECTORIES
|
|
58
65
|
end
|
|
59
|
-
end
|
|
60
66
|
|
|
61
|
-
def test_special_words_rspec
|
|
62
|
-
tsp = DidYouMean::TreeSpellChecker.new(dictionary: RSPEC_DIRECTORIES)
|
|
63
|
-
special_words_rspec.each do |word, word_error|
|
|
64
|
-
s = tsp.correct(word_error)
|
|
65
|
-
assert_match s.first, word
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def special_words_rspec
|
|
70
67
|
[
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
68
|
+
%w(test/spell_checking/variable_name_check_test.rb test/spell_checking/vriabl_ename_check_test.rb),
|
|
69
|
+
%w(test/spell_checking/key_name_check_test.rb tesit/spell_checking/key_name_choeck_test.rb),
|
|
70
|
+
].each do |expected, user_input|
|
|
71
|
+
assert_equal expected, DidYouMean::TreeSpellChecker.new(dictionary: MINI_DIRECTORIES).correct(user_input)[0]
|
|
72
|
+
end
|
|
76
73
|
end
|
|
77
74
|
|
|
78
|
-
def
|
|
75
|
+
def test_special_words_rspec
|
|
79
76
|
[
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
['test/edit_distance/jaro_winkler_test.rb', 'test/eidt_distance/jaro_winkler_test.@rb'],
|
|
88
|
-
['test/spell_checker_test.rb', 'test/spell_checke@r_test.rb'],
|
|
89
|
-
['test/tree_spell_human_typo_test.rb', 'testt/ree_spell_human_typo_test.rb'],
|
|
90
|
-
['test/spell_checking/variable_name_check_test.rb', 'test/spell_checking/vriabl_ename_check_test.rb'],
|
|
91
|
-
['test/spell_checking/key_name_check_test.rb', 'tesit/spell_checking/key_name_choeck_test.rb'],
|
|
92
|
-
['test/edit_distance/jaro_winkler_test.rb', 'test/edit_distance/jaro_winkler_tuest.rb']
|
|
93
|
-
]
|
|
77
|
+
%w(spec/rspec/core/formatters/exception_presenter_spec.rb spec/rspec/core/formatters/eception_presenter_spec.rb),
|
|
78
|
+
%w(spec/rspec/core/metadata_spec.rb spec/rspec/core/metadata_spe.crb),
|
|
79
|
+
%w(spec/rspec/core/ordering_spec.rb spec/spec/core/odrering_spec.rb),
|
|
80
|
+
%w(spec/support/mathn_integration_support.rb spec/support/mathn_itegrtion_support.rb),
|
|
81
|
+
].each do |expected, user_input|
|
|
82
|
+
assert_tree_spell expected, input: user_input, dictionary: RSPEC_DIRECTORIES
|
|
83
|
+
end
|
|
94
84
|
end
|
|
95
85
|
|
|
96
86
|
def test_file_in_root
|
|
97
|
-
|
|
98
|
-
word_error = 'test/spell_checker_test.r'
|
|
99
|
-
suggestions = DidYouMean::TreeSpellChecker.new(dictionary: MINI_DIRECTORIES).correct word_error
|
|
100
|
-
assert_equal word, suggestions.first
|
|
87
|
+
assert_tree_spell "test/spell_checker_test.rb", input: "test/spell_checker_test.r", dictionary: MINI_DIRECTORIES
|
|
101
88
|
end
|
|
102
89
|
|
|
103
90
|
def test_no_plausible_states
|
|
104
|
-
|
|
105
|
-
suggestions = DidYouMean::TreeSpellChecker.new(dictionary: MINI_DIRECTORIES).correct word_error
|
|
106
|
-
assert_equal [], suggestions
|
|
91
|
+
assert_tree_spell [], input: "testspell_checker_test.rb", dictionary: MINI_DIRECTORIES
|
|
107
92
|
end
|
|
108
93
|
|
|
109
94
|
def test_no_plausible_states_with_augmentation
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
assert_equal
|
|
95
|
+
assert_tree_spell [], input: "testspell_checker_test.rb", dictionary: MINI_DIRECTORIES
|
|
96
|
+
|
|
97
|
+
suggestions = DidYouMean::TreeSpellChecker.new(dictionary: MINI_DIRECTORIES, augment: true).correct("testspell_checker_test.rb")
|
|
98
|
+
|
|
99
|
+
assert_equal suggestions.first, "test/spell_checker_test.rb"
|
|
115
100
|
end
|
|
116
101
|
|
|
117
102
|
def test_no_idea_with_augmentation
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
assert_equal
|
|
103
|
+
assert_tree_spell [], input: "test/spell_checking/key_name.rb", dictionary: MINI_DIRECTORIES
|
|
104
|
+
|
|
105
|
+
suggestions = DidYouMean::TreeSpellChecker.new(dictionary: MINI_DIRECTORIES, augment: true).correct("test/spell_checking/key_name.rb")
|
|
106
|
+
|
|
107
|
+
assert_equal suggestions.first, "test/spell_checking/key_name_check_test.rb"
|
|
123
108
|
end
|
|
124
109
|
|
|
125
110
|
def test_works_out_suggestions
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
assert_equal suggestions.to_set, exp.to_set
|
|
111
|
+
assert_tree_spell %w(spec/models/concerns/vixen_spec.rb spec/models/concerns/vixenus_spec.rb),
|
|
112
|
+
input: "spek/modeks/confirns/viken_spec.rb",
|
|
113
|
+
dictionary: %w(spec/models/concerns/vixen_spec.rb spec/models/concerns/vixenus_spec.rb)
|
|
130
114
|
end
|
|
131
115
|
|
|
132
116
|
def test_works_when_input_is_correct
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
117
|
+
assert_tree_spell "spec/models/concerns/vixenus_spec.rb",
|
|
118
|
+
input: "spec/models/concerns/vixenus_spec.rb",
|
|
119
|
+
dictionary: @dictionary
|
|
136
120
|
end
|
|
137
121
|
|
|
138
122
|
def test_find_out_leaves_in_a_path
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
assert_equal
|
|
123
|
+
names = @tree_spell_checker.find_leaves("spec/modals/confirms")
|
|
124
|
+
|
|
125
|
+
assert_equal %w[abcd_spec.rb efgh_spec.rb], names
|
|
142
126
|
end
|
|
143
127
|
|
|
144
128
|
def test_works_out_nodes
|
|
145
|
-
exp_paths = [
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
129
|
+
exp_paths = ["spec/models/concerns",
|
|
130
|
+
"spec/models/confirms",
|
|
131
|
+
"spec/modals/concerns",
|
|
132
|
+
"spec/modals/confirms",
|
|
133
|
+
"spec/controllers/concerns",
|
|
134
|
+
"spec/controllers/confirms"]
|
|
135
|
+
|
|
136
|
+
states = @tree_spell_checker.dimensions
|
|
137
|
+
nodes = states[0].product(*states[1..-1])
|
|
138
|
+
paths = @tree_spell_checker.possible_paths(nodes)
|
|
139
|
+
|
|
140
|
+
assert_equal paths, exp_paths
|
|
155
141
|
end
|
|
156
142
|
|
|
157
143
|
def test_works_out_state_space
|
|
158
|
-
suggestions = @
|
|
159
|
-
|
|
144
|
+
suggestions = @tree_spell_checker.plausible_dimensions(@test_str)
|
|
145
|
+
|
|
146
|
+
assert_equal [["spec"], %w[models modals], %w[confirms concerns]], suggestions
|
|
160
147
|
end
|
|
161
148
|
|
|
162
149
|
def test_parses_dictionary
|
|
163
|
-
states = @
|
|
164
|
-
|
|
150
|
+
states = @tree_spell_checker.dimensions
|
|
151
|
+
|
|
152
|
+
assert_equal [["spec"], %w[models modals controllers], %w[concerns confirms]], states
|
|
165
153
|
end
|
|
166
154
|
|
|
167
155
|
def test_parses_elementary_dictionary
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
156
|
+
dimensions = DidYouMean::TreeSpellChecker
|
|
157
|
+
.new(dictionary: %w(spec/models/user_spec.rb spec/services/account_spec.rb))
|
|
158
|
+
.dimensions
|
|
159
|
+
|
|
160
|
+
assert_equal [["spec"], %w[models services]], dimensions
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
private
|
|
164
|
+
|
|
165
|
+
def assert_tree_spell(expected, input:, dictionary:, separator: "/")
|
|
166
|
+
suggestions = DidYouMean::TreeSpellChecker.new(dictionary: dictionary, separator: separator).correct(input)
|
|
167
|
+
|
|
168
|
+
assert_equal Array(expected), suggestions, "Expected to suggest #{expected}, but got #{suggestions.inspect}"
|
|
172
169
|
end
|
|
173
170
|
end
|
|
@@ -4,6 +4,9 @@ module TreeSpell
|
|
|
4
4
|
# Simulate an error prone human typist
|
|
5
5
|
# see doc/human_typo_api.md for the api description
|
|
6
6
|
class HumanTypo
|
|
7
|
+
POPULAR_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?<>,.!`+=-_":;@#$%^&*()'.split("").freeze
|
|
8
|
+
ACTION_TYPES = %i(insert transpose delete substitute).freeze
|
|
9
|
+
|
|
7
10
|
def initialize(input, lambda: 0.05)
|
|
8
11
|
@input = input
|
|
9
12
|
check_input
|
|
@@ -15,7 +18,7 @@ module TreeSpell
|
|
|
15
18
|
@word = input.dup
|
|
16
19
|
i_place = initialize_i_place
|
|
17
20
|
loop do
|
|
18
|
-
action =
|
|
21
|
+
action = ACTION_TYPES.sample
|
|
19
22
|
@word = make_change action, i_place
|
|
20
23
|
@len = word.length
|
|
21
24
|
i_place += exponential
|
|
@@ -41,40 +44,17 @@ module TreeSpell
|
|
|
41
44
|
(rand / (lambda / 2)).to_i
|
|
42
45
|
end
|
|
43
46
|
|
|
44
|
-
def rand_char
|
|
45
|
-
popular_chars = alphabetic_characters + special_characters
|
|
46
|
-
n = popular_chars.length
|
|
47
|
-
popular_chars[rand(n)]
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def alphabetic_characters
|
|
51
|
-
('a'..'z').to_a.join + ('A'..'Z').to_a.join
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def special_characters
|
|
55
|
-
'?<>,.!`+=-_":;@#$%^&*()'
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def toss
|
|
59
|
-
return +1 if rand >= 0.5
|
|
60
|
-
-1
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def action_type
|
|
64
|
-
[:insert, :transpose, :delete, :substitute][rand(4)]
|
|
65
|
-
end
|
|
66
|
-
|
|
67
47
|
def make_change(action, i_place)
|
|
68
48
|
cw = ChangeWord.new(word)
|
|
69
49
|
case action
|
|
70
50
|
when :delete
|
|
71
51
|
cw.deletion(i_place)
|
|
72
52
|
when :insert
|
|
73
|
-
cw.insertion(i_place,
|
|
53
|
+
cw.insertion(i_place, POPULAR_CHARS.sample)
|
|
74
54
|
when :substitute
|
|
75
|
-
cw.substitution(i_place,
|
|
55
|
+
cw.substitution(i_place, POPULAR_CHARS.sample)
|
|
76
56
|
when :transpose
|
|
77
|
-
cw.transposition(i_place,
|
|
57
|
+
cw.transposition(i_place, rand >= 0.5 ? +1 : -1)
|
|
78
58
|
end
|
|
79
59
|
end
|
|
80
60
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: did_you_mean
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yuki Nishijima
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2020-12-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rake
|
|
@@ -44,6 +44,7 @@ files:
|
|
|
44
44
|
- benchmark/levenshtein/memory_usage.rb
|
|
45
45
|
- benchmark/levenshtein/speed.rb
|
|
46
46
|
- benchmark/memory_usage.rb
|
|
47
|
+
- benchmark/require_path_checker.rb
|
|
47
48
|
- benchmark/speed.yml
|
|
48
49
|
- did_you_mean.gemspec
|
|
49
50
|
- documentation/CHANGELOG.md.erb
|
|
@@ -54,8 +55,6 @@ files:
|
|
|
54
55
|
- lib/did_you_mean.rb
|
|
55
56
|
- lib/did_you_mean/core_ext/name_error.rb
|
|
56
57
|
- lib/did_you_mean/experimental.rb
|
|
57
|
-
- lib/did_you_mean/experimental/initializer_name_correction.rb
|
|
58
|
-
- lib/did_you_mean/experimental/ivar_name_correction.rb
|
|
59
58
|
- lib/did_you_mean/formatters/plain_formatter.rb
|
|
60
59
|
- lib/did_you_mean/formatters/verbose_formatter.rb
|
|
61
60
|
- lib/did_you_mean/jaro_winkler.rb
|
|
@@ -67,6 +66,7 @@ files:
|
|
|
67
66
|
- lib/did_you_mean/spell_checkers/name_error_checkers/class_name_checker.rb
|
|
68
67
|
- lib/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb
|
|
69
68
|
- lib/did_you_mean/spell_checkers/null_checker.rb
|
|
69
|
+
- lib/did_you_mean/spell_checkers/require_path_checker.rb
|
|
70
70
|
- lib/did_you_mean/tree_spell_checker.rb
|
|
71
71
|
- lib/did_you_mean/verbose.rb
|
|
72
72
|
- lib/did_you_mean/version.rb
|
|
@@ -79,6 +79,7 @@ files:
|
|
|
79
79
|
- test/spell_checking/test_class_name_check.rb
|
|
80
80
|
- test/spell_checking/test_key_name_check.rb
|
|
81
81
|
- test/spell_checking/test_method_name_check.rb
|
|
82
|
+
- test/spell_checking/test_require_path_check.rb
|
|
82
83
|
- test/spell_checking/test_uncorrectable_name_check.rb
|
|
83
84
|
- test/spell_checking/test_variable_name_check.rb
|
|
84
85
|
- test/test_spell_checker.rb
|
|
@@ -108,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
108
109
|
- !ruby/object:Gem::Version
|
|
109
110
|
version: '0'
|
|
110
111
|
requirements: []
|
|
111
|
-
rubygems_version: 3.
|
|
112
|
+
rubygems_version: 3.1.3
|
|
112
113
|
signing_key:
|
|
113
114
|
specification_version: 4
|
|
114
115
|
summary: '"Did you mean?" experience in Ruby'
|
|
@@ -122,6 +123,7 @@ test_files:
|
|
|
122
123
|
- test/spell_checking/test_class_name_check.rb
|
|
123
124
|
- test/spell_checking/test_key_name_check.rb
|
|
124
125
|
- test/spell_checking/test_method_name_check.rb
|
|
126
|
+
- test/spell_checking/test_require_path_check.rb
|
|
125
127
|
- test/spell_checking/test_uncorrectable_name_check.rb
|
|
126
128
|
- test/spell_checking/test_variable_name_check.rb
|
|
127
129
|
- test/test_spell_checker.rb
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# frozen-string-literal: true
|
|
2
|
-
|
|
3
|
-
require_relative '../levenshtein'
|
|
4
|
-
|
|
5
|
-
module DidYouMean
|
|
6
|
-
module Experimental
|
|
7
|
-
module InitializerNameCorrection
|
|
8
|
-
def method_added(name)
|
|
9
|
-
super
|
|
10
|
-
|
|
11
|
-
distance = Levenshtein.distance(name.to_s, 'initialize')
|
|
12
|
-
if distance != 0 && distance <= 2
|
|
13
|
-
warn "warning: #{name} might be misspelled, perhaps you meant initialize?"
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
::Class.prepend(InitializerNameCorrection)
|
|
19
|
-
end
|
|
20
|
-
end
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
# frozen-string-literal: true
|
|
2
|
-
|
|
3
|
-
require_relative '../../did_you_mean'
|
|
4
|
-
|
|
5
|
-
module DidYouMean
|
|
6
|
-
module Experimental #:nodoc:
|
|
7
|
-
class IvarNameCheckerBuilder #:nodoc:
|
|
8
|
-
attr_reader :original_checker
|
|
9
|
-
|
|
10
|
-
def initialize(original_checker) #:nodoc:
|
|
11
|
-
@original_checker = original_checker
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def new(no_method_error) #:nodoc:
|
|
15
|
-
IvarNameChecker.new(no_method_error, original_checker: @original_checker)
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
class IvarNameChecker #:nodoc:
|
|
20
|
-
REPLS = {
|
|
21
|
-
"(irb)" => -> { Readline::HISTORY.to_a.last }
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
TRACE = TracePoint.trace(:raise) do |tp|
|
|
25
|
-
e = tp.raised_exception
|
|
26
|
-
|
|
27
|
-
if SPELL_CHECKERS.include?(e.class.to_s) && !e.instance_variable_defined?(:@frame_binding)
|
|
28
|
-
e.instance_variable_set(:@frame_binding, tp.binding)
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
attr_reader :original_checker
|
|
33
|
-
|
|
34
|
-
def initialize(no_method_error, original_checker: )
|
|
35
|
-
@original_checker = original_checker.new(no_method_error)
|
|
36
|
-
|
|
37
|
-
@location = no_method_error.backtrace_locations.first
|
|
38
|
-
@ivar_names = no_method_error.frame_binding.receiver.instance_variables
|
|
39
|
-
|
|
40
|
-
no_method_error.remove_instance_variable(:@frame_binding)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def corrections
|
|
44
|
-
original_checker.corrections + ivar_name_corrections
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def ivar_name_corrections
|
|
48
|
-
@ivar_name_corrections ||= SpellChecker.new(dictionary: @ivar_names).correct(receiver_name.to_s)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
private
|
|
52
|
-
|
|
53
|
-
def receiver_name
|
|
54
|
-
return unless @original_checker.receiver.nil?
|
|
55
|
-
|
|
56
|
-
abs_path = @location.absolute_path
|
|
57
|
-
lineno = @location.lineno
|
|
58
|
-
|
|
59
|
-
/@(\w+)*\.#{@original_checker.method_name}/ =~ line(abs_path, lineno).to_s && $1
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def line(abs_path, lineno)
|
|
63
|
-
if REPLS[abs_path]
|
|
64
|
-
REPLS[abs_path].call
|
|
65
|
-
elsif File.exist?(abs_path)
|
|
66
|
-
File.open(abs_path) do |file|
|
|
67
|
-
file.detect { file.lineno == lineno }
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
NameError.send(:attr, :frame_binding)
|
|
75
|
-
SPELL_CHECKERS['NoMethodError'] = Experimental::IvarNameCheckerBuilder.new(SPELL_CHECKERS['NoMethodError'])
|
|
76
|
-
end
|