From e5a73815f580d296572e11b71b5f3ed320bbe912 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Fri, 30 May 2014 12:42:48 +0100 Subject: Add helper to highlight and excerpt by regex Backport of https://github.com/rails/rails/pull/11793/ Contains integration tests to check that it works as expected with ActsAsXapian. --- app/helpers/highlight_helper.rb | 92 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 app/helpers/highlight_helper.rb (limited to 'app/helpers/highlight_helper.rb') diff --git a/app/helpers/highlight_helper.rb b/app/helpers/highlight_helper.rb new file mode 100644 index 000000000..63809aff5 --- /dev/null +++ b/app/helpers/highlight_helper.rb @@ -0,0 +1,92 @@ +module HighlightHelper + + # Implementation of rails' highlight that allows regex to be passed to + # the phrases parameter. + # https://github.com/rails/rails/pull/11793 + def highlight_matches(text, phrases, options = {}) + text = ActionController::Base.helpers.sanitize(text).try(:html_safe) if options.fetch(:sanitize, true) + + if text.blank? || phrases.blank? + text + else + highlighter = options.fetch(:highlighter, '\1') + match = Array(phrases).map do |p| + Regexp === p ? p.to_s : Regexp.escape(p) + end.join('|') + text.gsub(/(#{match})(?![^<]*?>)/i, highlighter) + end.html_safe + end + + # Highlight words, also escapes HTML (other than spans that we add) + def highlight_words(t, words, html = true) + if html + highlight_matches(h(t), words, :highlighter => '\1').html_safe + else + highlight_matches(t, words, :highlighter => '*\1*') + end + end + + def highlight_and_excerpt(t, words, excount, html = true) + newt = excerpt(t, words[0], :radius => excount) + if not newt + newt = excerpt(t, '', :radius => excount) + end + t = newt + t = highlight_words(t, words, html) + return t + end + + def excerpt(text, phrase, options = {}) + return unless text && phrase + + separator = options.fetch(:separator, nil) || "" + if Regexp === phrase + regex = phrase + else + phrase = Regexp.escape(phrase) + regex = /#{phrase}/iu + end + + return unless matches = text.match(regex) + phrase = matches[0] + + unless separator.empty? + text.split(separator).each do |value| + if value.match(regex) + regex = phrase = value + break + end + end + end + + first_part, second_part = text.split(regex, 2) + + prefix, first_part = cut_excerpt_part(:first, first_part, separator, options) + postfix, second_part = cut_excerpt_part(:second, second_part, separator, options) + + affix = [first_part, separator, phrase, separator, second_part].join.strip + [prefix, affix, postfix].join + end + + private + + def cut_excerpt_part(part_position, part, separator, options) + return "", "" unless part + + radius = options.fetch(:radius, 100) + omission = options.fetch(:omission, "...") + + part = part.split(separator) + part.delete("") + affix = part.size > radius ? omission : "" + + part = if part_position == :first + drop_index = [part.length - radius, 0].max + part.drop(drop_index) + else + part.first(radius) + end + + return affix, part.join(separator) + end +end -- cgit v1.2.3 From 4d9c89d0e416825eb52a720d74230f547454ba31 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Fri, 30 May 2014 15:48:10 +0100 Subject: Use regex based highlighting --- app/helpers/highlight_helper.rb | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) (limited to 'app/helpers/highlight_helper.rb') diff --git a/app/helpers/highlight_helper.rb b/app/helpers/highlight_helper.rb index 63809aff5..06ade48ee 100644 --- a/app/helpers/highlight_helper.rb +++ b/app/helpers/highlight_helper.rb @@ -6,14 +6,19 @@ module HighlightHelper def highlight_matches(text, phrases, options = {}) text = ActionController::Base.helpers.sanitize(text).try(:html_safe) if options.fetch(:sanitize, true) - if text.blank? || phrases.blank? - text - else - highlighter = options.fetch(:highlighter, '\1') - match = Array(phrases).map do |p| - Regexp === p ? p.to_s : Regexp.escape(p) - end.join('|') - text.gsub(/(#{match})(?![^<]*?>)/i, highlighter) + if text.blank? || phrases.blank? + text + else + match = Array(phrases).map do |p| + Regexp === p ? p.to_s : Regexp.escape(p) + end.join('|') + + if block_given? + text.gsub(/(#{match})(?![^<]*?>)/i) { |found| yield found } + else + highlighter = options.fetch(:highlighter, '\1') + text.gsub(/(#{match})(?![^<]*?>)/i, highlighter) + end end.html_safe end @@ -40,11 +45,11 @@ module HighlightHelper return unless text && phrase separator = options.fetch(:separator, nil) || "" - if Regexp === phrase + case phrase + when Regexp regex = phrase else - phrase = Regexp.escape(phrase) - regex = /#{phrase}/iu + regex = /#{Regexp.escape(phrase)}/i end return unless matches = text.match(regex) @@ -59,7 +64,7 @@ module HighlightHelper end end - first_part, second_part = text.split(regex, 2) + first_part, second_part = text.split(phrase, 2) prefix, first_part = cut_excerpt_part(:first, first_part, separator, options) postfix, second_part = cut_excerpt_part(:second, second_part, separator, options) -- cgit v1.2.3 From e490c4a7ec7157e794d849c962371e298d8342d9 Mon Sep 17 00:00:00 2001 From: Gareth Rees Date: Tue, 10 Jun 2014 09:35:32 +0100 Subject: Add spec for highlight_and_excerpt --- app/helpers/highlight_helper.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'app/helpers/highlight_helper.rb') diff --git a/app/helpers/highlight_helper.rb b/app/helpers/highlight_helper.rb index 06ade48ee..a98f6f320 100644 --- a/app/helpers/highlight_helper.rb +++ b/app/helpers/highlight_helper.rb @@ -1,4 +1,5 @@ module HighlightHelper + include ERB::Util # Implementation of rails' highlight that allows regex to be passed to # the phrases parameter. -- cgit v1.2.3