Apps may have thousands of candidate templates so we attempt to generate
the suggestions as efficiently as possible. First we split templates into
prefixes and basenames, so that those can be matched separately.
# File actionview/lib/action_view/template/error.rb, line 93
def corrections
candidates = paths.flat_map(&:all_template_paths).uniq
if partial
candidates.select!(&:partial?)
else
candidates.reject!(&:partial?)
end
# Group by possible prefixes
files_by_dir = candidates.group_by(&:prefix)
files_by_dir.transform_values! do |files|
files.map do |file|
# Remove prefix
File.basename(file.to_s)
end
end
# No suggestions if there's an exact match, but wrong details
if prefixes.any? { |prefix| files_by_dir[prefix]&.include?(path) }
return []
end
cached_distance = Hash.new do |h, args|
h[args] = -DidYouMean::Jaro.distance(*args)
end
results = Results.new(6)
files_by_dir.keys.index_with do |dirname|
prefixes.map do |prefix|
cached_distance[[prefix, dirname]]
end.min
end.sort_by(&:last).each do |dirname, dirweight|
# If our directory's score makes it impossible to find a better match
# we can prune this search branch.
next unless results.should_record?(dirweight - 1.0)
files = files_by_dir[dirname]
files.each do |file|
fileweight = cached_distance[[path, file]]
score = dirweight + fileweight
results.add(File.join(dirname, file), score)
end
end
if partial
results.to_a.map { |res| res.sub(%{_([^/]+)\z}, "\\1") }
else
results.to_a
end
end