Files
danbooru/app/components/autocomplete_component.rb
evazion a80ee22746 autocomplete: fix incorrect highlighting when tag contains repeated words.
Fix tags like `short_shorts` or `hunter_x_hunter` being highlighted
incorrectly. Typing `short_sh` would highlight it as SHort_SHorts
instead of as SHORT_SHorts.
2022-09-06 18:01:30 -05:00

90 lines
3.4 KiB
Ruby

# frozen_string_literal: true
class AutocompleteComponent < ApplicationComponent
attr_reader :autocomplete_service
delegate :humanized_number, to: :helpers
delegate :autocomplete_results, :query, :metatag, to: :autocomplete_service
def initialize(autocomplete_service:)
@autocomplete_service = autocomplete_service
end
def link_to_result(result, &block)
case result.type
when "user", "mention"
link_to user_path(result.id), class: "user-#{result.level}", "@click.prevent": "", &block
when "pool"
link_to pool_path(result.id), class: "pool-category-#{result.category}", "@click.prevent": "", &block
else
link_to posts_path(tags: result.value), class: "tag-type-#{result.category}", "@click.prevent": "", &block
end
end
def highlight_antecedent(result)
if result.type == "tag-word"
highlight_matching_words(result.antecedent, query)
elsif query.include?("*")
highlight_wildcard_match(result.antecedent, query)
else
highlight_wildcard_match(result.antecedent, query + "*")
end
end
def highlight_result(result)
if metatag.present? && metatag.value.include?("*")
highlight_wildcard_match(result.label, metatag.value)
elsif metatag.present? && metatag.name.in?(%w[pool favgroup])
highlight_wildcard_match(result.label, "*" + metatag.value + "*")
elsif metatag.present? && metatag.name.in?(%w[ai unaliased])
highlight_matching_words(result.label, metatag.value)
elsif metatag.present?
highlight_wildcard_match(result.label, metatag.value + "*")
elsif result.type == "tag-word"
highlight_matching_words(result.value, query)
elsif result.type == "mention"
highlight_wildcard_match(result.label, query + "*")
elsif query.include?("*")
highlight_wildcard_match(result.value, query)
else
highlight_wildcard_match(result.value, query + "*")
end
end
# Highlight the words in the `target` string matching the words in the search `pattern`.
#
# highlight_matching_words("very_long_hair", "long_ha*") => "<span>very_</span><b>long</b><span>_</span><b>hair</b>"
def highlight_matching_words(target, pattern)
pattern_words = Tag.parse_query(pattern)
pattern_words.sort_by! { |word| [word.include?("*") ? 1 : 0, -word.size] }
target_words = Tag.split_words(target)
target_words.map do |word|
pat = pattern_words.find { |pat| word.ilike?(pat) }
highlight_wildcard_match(word, pat)
end.join("").html_safe
end
# Highlight the parts of the `target` string that match the wildcard search `pattern`.
#
# highlight_wildcard_match("very_long_hair", "*long*") => "<span>very_</span><b>long</b><span>_hair</span>"
def highlight_wildcard_match(target, pattern)
return tag.span(target.tr("_", " ")) if !target.ilike?(pattern.to_s)
words = pattern.split(/(\*)/).compact_blank # "*black*" => ["*", "black", "*"]
regexp = words.map { |w| w == "*" ? "(.*)" : "(#{Regexp.escape(w)})" }.join # "*black*" => "(.*)(black)(.*)"
regexp = Regexp.new(regexp, "i")
captures = target.match(regexp).captures # "black_thighhighs" =~ /(.*)(black)(.*)/ => ["", "black", "_thighhighs"]
captures.zip(words).map do |substring, word|
if substring == ""
""
elsif word == "*"
tag.span(substring.tr("_", " "))
else
tag.b(substring.tr("_", " "))
end
end.join.html_safe
end
end