From bb96d631537d3d8181f0d3b762603a012219c3e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D=C3=A1vila?= Date: Wed, 30 Dec 2015 00:52:50 -0500 Subject: New implementation for highlighting diff files. #3945 * It is more performant given now we process all the diff file instead of processing line by line. * Multiline comments are highlighted correctly. --- lib/gitlab/diff/file.rb | 4 +++ lib/gitlab/diff/highlight.rb | 55 +++++++++++++++++++++++++++++++++++++++++ lib/gitlab/diff/line.rb | 1 + lib/rouge/lexers/gitlab_diff.rb | 2 +- 4 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 lib/gitlab/diff/highlight.rb (limited to 'lib') diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb index 79061cd0141..ff8765b8e26 100644 --- a/lib/gitlab/diff/file.rb +++ b/lib/gitlab/diff/file.rb @@ -15,6 +15,10 @@ module Gitlab @lines ||= parser.parse(raw_diff.lines) end + def highlighted_diff_lines + Gitlab::Diff::Highlight.process_diff_lines(self) + end + def mode_changed? !!(diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode) end diff --git a/lib/gitlab/diff/highlight.rb b/lib/gitlab/diff/highlight.rb new file mode 100644 index 00000000000..f10b55eb00b --- /dev/null +++ b/lib/gitlab/diff/highlight.rb @@ -0,0 +1,55 @@ +module Gitlab + module Diff + class Highlight + def self.process_diff_lines(diff_file) + processor = new(diff_file) + processor.highlight + end + + def initialize(diff_file) + text_lines = diff_file.diff_lines.map(&:text) + @diff_file = diff_file + @diff_lines = diff_file.diff_lines + @diff_line_prefixes = text_lines.map { |line| line.sub!(/\A((\+|\-)\s*)/, '');$1 } + @raw_lines = text_lines.join("\n") + end + + def highlight + @code = unescape_html(@raw_lines) + @highlighted_code = formatter.format(lexer.lex(@code)) + + update_diff_lines + end + + private + + def update_diff_lines + @highlighted_code.lines.each_with_index do |line, i| + @diff_lines[i].highlighted_text = "#{@diff_line_prefixes[i]}#{line}" + end + + @diff_lines + end + + def lexer + parent = Rouge::Lexer.guess(filename: @diff_file.new_path, source: @code).new rescue Rouge::Lexers::PlainText.new + Rouge::Lexers::GitlabDiff.new(parent_lexer: parent) + end + + def unescape_html(content) + text = CGI.unescapeHTML(content) + text.gsub!(' ', ' ') + text + end + + def formatter + @formatter ||= Rouge::Formatters::HTMLGitlab.new( + nowrap: true, + cssclass: 'code highlight', + lineanchors: true, + lineanchorsid: 'LC' + ) + end + end + end +end diff --git a/lib/gitlab/diff/line.rb b/lib/gitlab/diff/line.rb index 0072194606e..c48c69fb344 100644 --- a/lib/gitlab/diff/line.rb +++ b/lib/gitlab/diff/line.rb @@ -2,6 +2,7 @@ module Gitlab module Diff class Line attr_reader :type, :text, :index, :old_pos, :new_pos + attr_accessor :highlighted_text def initialize(text, type, index, old_pos, new_pos) @text, @type, @index = text, type, index diff --git a/lib/rouge/lexers/gitlab_diff.rb b/lib/rouge/lexers/gitlab_diff.rb index c7aaeb92608..d91dd6c4245 100644 --- a/lib/rouge/lexers/gitlab_diff.rb +++ b/lib/rouge/lexers/gitlab_diff.rb @@ -11,7 +11,7 @@ module Rouge token InlineDiff, match[1] end - rule /(?:(?!