-
-
Notifications
You must be signed in to change notification settings - Fork 753
Conversation
The functionality provides for suggestions when a person enters an rspec spec/some_file_pathcommand with typographical errors. If there are errors, then at lib/rspec/core/configuration.rb:2037 the code searches for suggestions. If useful suggestions are found they are added to the reported exception at lib/rspec/core/configuration.rb:2038. The suggestions are done by the DidYouMean::Suggestions service object, which calls the DidYouMean::Proximity service object. These are found in lib/rspec/core/did_you_mean The other change to the current code is to provide for autoloading of the DidYouMean constants, this is done at lib/rspec/core.rb:142. Two test files are added in the spec/rspec/core/did_you_mean directory. This functionality was inspired by https://github.com/yuki24/did_you_mean and partly based on the jaro_winkler code from that source https://github.com/yuki24/did_you_mean/blob/master /lib/did_you_mean/jaro_winkler.rb. This code has an MIT licence.
Add comments for failing yard tests Add work arounds for early rubies and jruby
Because of lack of robust support for UTF-8
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work so far, just had a first pass of feedback
lib/rspec/core.rb
Outdated
@@ -139,6 +139,7 @@ def self.world | |||
module Core | |||
autoload :ExampleStatusPersister, "rspec/core/example_status_persister" | |||
autoload :Profiler, "rspec/core/profiler" | |||
autoload :DidYouMean, "rspec/core/did_you_mean/did_you_mean" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might as well line this up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -1,4 +1,4 @@ | |||
# This file was generated on 2019-01-03T20:34:23+00:00 from the rspec-dev repo. | |||
# This file was generated on 2019-01-08T11:28:05+00:00 from the rspec-dev repo. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if you rebase this commit will go away
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume you are talking about the commit #2591 with sha 511cec5. I am concerned that if I rebase this on my local machine and then push it, then the will be problems as I have already pushed this up. As well, if I look at rspec-core master, #2591 has a sha of cb1b4ce If I do a diff between my local did_you_mean branch and my local master it is not showing any files from 511cec5. Frankly, I do not understand what is happening here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its because its based off an older branch, github does that. You can use git push <origin> <branch> --force-with-lease
to update this branch safely after a rebase, it won't let you ruin anything then.
autoload :Suggestions, 'rspec/core/did_you_mean/suggestions' | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we ditch the two child files and have it all in here please.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. I also ditched the directory given there is now only one file.
end | ||
|
||
def red_font(mytext) | ||
"\e[31m#{mytext}\e[0m" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can't colourise text like this, we have configurable colours and the reporter should take care of it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As you will see, in my did_you_mean.red_font, I now use the coloriser.wrap method to do the colouring. Is this OK, or do you want me to use the reporter more directly, say by modifying reporter. notify_non_example_exception
to take care of this?
module DidYouMean | ||
RSpec.describe Proximity do | ||
# port of https://github.com/yuki24/did_you_mean/blob/master/test/edit_distance/jaro_winkler_test.rb | ||
if RUBY_VERSION.to_f >= 2.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have functions in RSpec::Support to take care of things like this, see how it does the specs for encoded strings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked at encoded string spec and it seems to use a similar approach (at line 88) as I am using. Am I looking in the right place?
However, I do wonder about the exact implementation being used at line 88. In particular, using RUBY_VERSION < '2.1'
appears to rely on a string comparison which can give incorrect results e.g '11' < '2.1' evaluates as true.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The key difference is we use the function we need first, (for example line 43) then only use RUBY_VERSION
for things we can't pick out by other means, we also gate this functionality out for the other gems via https://github.com/rspec/rspec-support/blob/master/lib/rspec/support/ruby_features.rb
.
However, I do wonder about the exact implementation being used at line 88. In particular, using
RUBY_VERSION < '2.1'
appears to rely on a string comparison which can give incorrect results e.g '11' < '2.1' evaluates as true.
It does, however RUBY_VERSION
will always by x.y.z
unless tampered with, and why we prefer feature detection instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, I will have a further look.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done, I think?
|
||
# provide probable suggestions if a LoadError | ||
def call | ||
return unless RUBY_VERSION.to_f >= 2.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't typically do ruby version comparison at runtime, but instead define alternate methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
# provide probable suggestions if a LoadError | ||
def call | ||
return unless RUBY_VERSION.to_f >= 2.0 | ||
return unless exception.class == LoadError |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably shouldn't call this at all if we don't have the error we expect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
private | ||
|
||
def formats(short_list) | ||
rspec_format = short_list.map { |s, _| "rspec ./#{s}" } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we have some logic for this somewhere
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have not been able to find it. Is your concern that sometimes the correct prefix is not 'rspec ./'?
module RSpec | ||
module Core | ||
module DidYouMean | ||
# class to calculate jaro_winkler distance based on |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm unsure if this is worth this complexity, is there not a similar simpler algorithm we can use?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Levenshtein is simpler to implement, might have to benchmark the two for comparison and see what suits better, and whats faster https://github.com/ioquatix/build-text/blob/master/lib/build/text/merge.rb#L60-L102
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And it's there already in RubyGems:
> require 'rubygems/text'
> include Gem::Text
> levenshtein_distance 'abcd', 'sdff' # => 4
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mm, we can't use that directly, as we don't depend on rubygems and its not always available. We could however make this contextual on that, (e.g. disable the feature on load error.
module RSpec
module Core
module DidYouMean
begin
require 'rubygems/text'
# ... define did you mean logic
rescue LoadError
# ... define identical public entry point which returns the old behaviour
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am still analysing levenshtein vs jaro-winkler so will comment on the idea of the using the text gem when I have completed the analysis.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have implemented the levenshtein and did a simple benchmark. When run on the rspec-core files, levenshtein takes about 100ms, whereas jaro-winkler took 17ms. Levenshtein is clearly O(n^2) where n
is the string length, but according to this article jaro-winkler is O(n). This means if some projects are deeply nested i.e. have longer strings for the file names, then jaro-winkler will have an ever greater speed improvement over levenshtein.
Just based on my very limited testing, I think jaro-winkler may give better suggestions than levensthein though this may becasause of the cut_offs I have set.
As well, although the levensthein algorithm is shorter, it is a very clever bit of logic that uses dynamic programming to find the minimum number of inserts/deletions/substitutions to transform one string to another. By contrast jaro-winkler is simply counting the number of matches and transpositions so is conceptually much simpler. As well I think I can simplify the jaro-winkler algorithm by removing the winkler part which gives priority over the first four characters which in our case is always 'spec'.
So I suggest that we go with a simplified jaro-winkler. But I am happy to go with levenshtein, and if we do that I will consider the point about using the text gem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The total time for the did_you_mean is directly proportional to the number of files in the spec folder. If we go with jaro-winkler this is about 0.2 ms per file. For 1,000 files this is about 0.2 seconds which would not be perceptible, but for 10,000 files it would be 2 seconds which would cause a marked delay. If you think some users will have more than 1,000 files, I suggest I develop a test to determine the expected time and not look for suggestions if the time is greater than about 500ms. Please let me know if you think I should do this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As suggested by palkan at #2590 I have changed to using the DidYouMean api. Using the DidYouMean api, a typical check takes about 6ms on my machine. If I put in a really bad long and unlikely string, it takes 17ms. On average it is much, much faster than Levenstein and faster than my implementation of jaro-winkler. It seems to give good suggestions and is much simpler.
Jon, thank you for your suggestions, I will work through them over the next week or two. Chris |
Use proximity rather than nearness Refactor did_you_mean to one file Remove legacy did_you_mean folders Add tests for levenshtein Add tests for private methods Test for LoadError in configuration.rb Refactor ruby version test to use defined? codepoints
I am still failing the early ruby tests. I use rvm and it will not let me install 1.9.2 or earlier, so I have no way of testing early rubies locally. How do people working on rspec-core solve this problem. |
…into did_you_mean
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work, mostly stylistic feedback this time :)
lib/rspec/core/did_you_mean.rb
Outdated
@@ -0,0 +1,44 @@ | |||
module RSpec | |||
module Core | |||
# Service object to provide did_you_mean suggestions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unless you make this object yard private # @private
you'll need to document all the methods in this file with yard, but I'm happy for this to be a private API. I'd suggest an improvement to this comment however.
# Wrapper around Ruby's `DidYouMean::SpellChecker` when available to provide file name suggestions.
As all of RSpec is "service" objects., and did_you_mean
isn't a code path for us :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
lib/rspec/core/did_you_mean.rb
Outdated
end | ||
|
||
if defined?(::DidYouMean::SpellChecker) | ||
# provide probable suggestions if a LoadError |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing checks it's a load error here, so perhaps this comment is unnecessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
lib/rspec/core/did_you_mean.rb
Outdated
formats probables | ||
end | ||
else # ruby 2.3.2 or less | ||
# return nil if API for ::DidYouMean::SpellChecker not supported |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should return an empty string, or maybe even a warning?, I feel the ruby comment is irrelevant, its if the library is available or not (which could also happens if someone uninstalls a standard gem).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What type of warning do you think we should we use? Don't think it will add noise?
Do you consider something like:
'did_you_mean' standard gem is not installed. It could help you to solve the loading failure
An error occurred while loading /folder/file.rb.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Outputting:
"\nHint: Install the `did_you_mean` gem in order to provide suggestions for similarly named files."
Would produce:
An error occurred while loading #{relative_file}.
Hint: Install the `did_you_mean` gem in order to provide suggestions for similarly named files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems a good idea!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
require 'tmpdir' | ||
require 'rspec/support/spec/in_sub_process' | ||
|
||
module RSpec |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We favour RSpec::Core
style name spacing, saves some spaces :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I prefer it too, but when I used it, I encountered the following cop:
Style/ClassAndModuleChildren: Use nested module/class definitions instead of compact style.
When I investigated further, I found this description of why the cop is there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Weird, we do that style elsewhere, is it possible you're using more updated version of Rubocop than us? Or not running with our config?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am using 0.52.1
, the same as in the Gemfile. I looked at .rubocop.yml and it has the following entry for this cop:
# Not sure what to do with this rule yet.
Style/ClassAndModuleChildren:
Exclude:
- lib/rspec/core/formatters.rb
- lib/rspec/core/notifications.rb
- lib/rspec/core/option_parser.rb
- lib/rspec/core/reporter.rb
so I can either add did_you_mean.rb
to this list, or leave the nested modules syntax in?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No its not a blocker
spec/rspec/core/did_you_mean_spec.rb
Outdated
@@ -0,0 +1,41 @@ | |||
require 'tmpdir' | |||
require 'rspec/support/spec/in_sub_process' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you using this/these still?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed
spec/rspec/core/did_you_mean_spec.rb
Outdated
module Core | ||
RSpec.describe DidYouMean do | ||
if defined?(::DidYouMean::SpellChecker) | ||
describe 'Call to DidYouMean' do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We typically do describe "#method_name"
for the outer, so #call
in this case, with inner contexts so ideally this spec would have the layout, you can also use hide_const
to test the functionality.
describe "#call" do
if defined?(::DidYouMean::SpellChecker)
context "when `DidYouMean::SpellChecker` is available", skip: defined?(::DidYouMean::SpellChecker) do
# ...
end
context "when `DidYouMean::SpellChecker` is not available" do
before do
hide_const("DidYouMean::SpellChecker") if defined?(DidYouMean::SpellChecker)
end
# ...
end
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is neat, I was not aware of the hide_const. I have implemented this change, but had one puzzle. When I run rspec spec
then I get following error:
RSpec::Core::DidYouMean#call when `DidYouMean::SpellChecker` is not available Success returns a hint
Failure/Error: checker = ::DidYouMean::SpellChecker.new(:dictionary => Dir["spec/**/*.rb"])
NameError:
uninitialized constant DidYouMean::SpellChecker
Did you mean? DidYouMean::SpellChecker
DidYouMean::SPELL_CHECKERS
# ./lib/rspec/core/did_you_mean.rb:15:in `call'
# ./spec/rspec/core/did_you_mean_spec.rb:34:in `block (5 levels) in <module:Core>'
but when I run script/run_build
all rspec tests pass. Is there some filter or setting I should be adding when I do rspec spec
on my local machine?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CI tests have finished and it appears that the :skip
keyword is not supported by the rspec that runs with the earlier rubies e.g. 1.8.7. Any suggestions what to do about this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking about the fact I get the error when running rspec spec
but not when I run script/run_build
, I think that although ruby is an interpreted language, there seems to be a two pass process that can see that the if defined?(::DidYouMean::SpellChecker)
at did_you_mean:12, can be decided ahead of time.
The issue seems to be that when I run 'rspec spec' this is done after the did_you_mean_spec.rb
code runs but before the did_you_mean.rb
code runs. Whereas when I do script/run_build
, the static compilation step is done prior to the did_you_mean_spec.rb
code running. Does this make sense?
spec/rspec/core/did_you_mean_spec.rb
Outdated
describe 'Call to DidYouMean' do | ||
describe 'Success' do | ||
let(:name) { './spec/rspec/core/did_you_mean_spec.rb' } | ||
it 'should return a useful suggestion' do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We prefer positive text around expectations, eg. it 'returns a useful suggestion'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
spec/rspec/core/did_you_mean_spec.rb
Outdated
end | ||
end | ||
context 'No suitable suggestions' do | ||
it 'Whereof one cannot speak, thereof one must be silent' do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we have something a bit er... plainer here? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, not everyone is a Wittgenstein fan. Done.
spec/rspec/core/did_you_mean_spec.rb
Outdated
RSpec.describe DidYouMean do | ||
describe '#call' do | ||
if defined?(::DidYouMean::SpellChecker) | ||
context "when `DidYouMean::SpellChecker` is available", skip: !defined?(::DidYouMean::SpellChecker) do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For 1.8.7 etc this needs to be :skip =>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Of course. Done
spec/rspec/core/did_you_mean_spec.rb
Outdated
module Core | ||
RSpec.describe DidYouMean do | ||
describe '#call' do | ||
if defined?(::DidYouMean::SpellChecker) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should be able to remove this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You need to remove this so the tests run when not defined for not defined
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
spec/rspec/core/did_you_mean_spec.rb
Outdated
end | ||
context "when `DidYouMean::SpellChecker` is not available" do | ||
before do | ||
hide_const("::DidYouMean::SpellChecker") if defined?(::DidYouMean::SpellChecker) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't seem to work on windows, I wonder if we should should do :unless => defined?(::DidYouMean::SpellChecker)
on the context instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking great! I would have merged it except for the windows CI failures, can you take a look?
The windows CI failures have the same pattern of failure that I see on my local machine when I run |
I have added the The DidYouMean api uses JaroWinkler as the major filter. This means it gives too much priority to errors early in the string and the thresholds are too low for the length of string that we are using. Accordingly, I re-introduced two ad-hoc fixes for this that I used in my first implementation. At a more fundamental level, the DidYouMean algorithm appears to be designed for short string lengths, say 1 to 15 characters and an unstructured dictionary. The strings we are using are much longer. On the other hand, the dictionary that we are using is highly structured. Each path e.g. We could develop an ad hoc approach to solving this, for instance palkans approach is an example that just uses the last node in the chain. I have been thinking about this, and think the problem may well lend itself to an elegant hidden markov model type approach, as well it would be interesting to see what other people done with structured dictionaries. However, this is more a research project, it could take some time to get right, the code to implement such an algorithm would be of use to other programs e.g. minitest and cucumber, and code would be much more complex that the present implementation. I suggest for the time being we go with what has been developed which does appear to be useful. After this has gone to the master branch, I will raise an issue on the DidYouMean gem to develop an option for the api to use a structured dictionary. If this is accepted and I or other people do develop such an option, then we would just need to slightly modify the our DidYouMean api to use this structured option. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good job! LGTM!
Thanks for the detail last comment
JonRowe, would you like me to do anything further? |
Thanks! ❤️ 💚 💛 💜 |
Thank you JonRowe for your thoughtful reviewing. |
* Add did_you_mean functionality to rspec-core. The functionality provides for suggestions when a person enters an rspec spec/some_file_path command with typographical errors. If there are errors, then the code at lib/rspec/core/configuration.rb:2037 searches for suggestions. If useful suggestions are found they are added to the reported exception at lib/rspec/core/configuration.rb:2038. The suggestions are done by the ::DidYouMean::SpellChecker API where available.
* Add did_you_mean functionality to rspec-core. The functionality provides for suggestions when a person enters an rspec spec/some_file_path command with typographical errors. If there are errors, then the code at lib/rspec/core/configuration.rb:2037 searches for suggestions. If useful suggestions are found they are added to the reported exception at lib/rspec/core/configuration.rb:2038. The suggestions are done by the ::DidYouMean::SpellChecker API where available. --- This commit was imported from rspec/rspec-core@cbd6d47.
This commit was imported from rspec/rspec-core@f17a414.
This PR is to add
Did you mean?
type functionality torspec
such that if an incorrect path is provided to therspec
command then suggestions will be made as to the correct path. A brief summary of of the changes needed to do this can be seen in the PR commit messages.I welcome questions, suggestions or feedback in regard to this PR.