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: [PATCH] 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. --- app/helpers/application_helper.rb | 6 -- app/helpers/blob_helper.rb | 15 ----- app/helpers/diff_helper.rb | 6 +- .../projects/diffs/_parallel_view.html.haml | 6 +- app/views/projects/diffs/_text_file.html.haml | 5 +- 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 +- spec/helpers/blob_helper_spec.rb | 11 ---- 10 files changed, 68 insertions(+), 43 deletions(-) create mode 100644 lib/gitlab/diff/highlight.rb diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index bc4b6ec0327..0b00b9a0702 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -326,10 +326,4 @@ module ApplicationHelper def truncate_first_line(message, length = 50) truncate(message.each_line.first.chomp, length: length) if message end - - def unescape_html(content) - text = CGI.unescapeHTML(content) - text.gsub!(' ', ' ') - text - end end diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index bf18673972c..1230002e69c 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -24,21 +24,6 @@ module BlobHelper result end - def highlight_line(blob_name, content, continue: false) - if @previous_blob_name != blob_name - @parent = Rouge::Lexer.guess(filename: blob_name, source: content).new rescue Rouge::Lexers::PlainText.new - @lexer = Rouge::Lexers::GitlabDiff.new(parent_lexer: @parent) - @options = Rouge::Lexers::PlainText === @parent ? {} : { continue: continue } - end - - @previous_blob_name = blob_name - @formatter ||= rouge_formatter(nowrap: true) - - content.sub!(/\A((?:\+|-)\s*)/, '') # Don't format '+' or '-' indicators. - - "#{$1}#{@formatter.format(@lexer.lex(content, @options))}".html_safe - end - def no_highlight_files %w(credits changelog news copying copyright license authors) end diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index 24134310fc5..2ff8c65e5ca 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -54,9 +54,9 @@ module DiffHelper # right_line_type, right_line_number, right_line_content, right_line_code # ] # - diff_file.diff_lines.each do |line| + diff_file.highlighted_diff_lines.each do |line| - full_line = line.text + full_line = line.highlighted_text type = line.type line_code = generate_line_code(diff_file.file_path, line) line_new = line.new_pos @@ -67,7 +67,7 @@ module DiffHelper if next_line next_line_code = generate_line_code(diff_file.file_path, next_line) next_type = next_line.type - next_line = next_line.text + next_line = next_line.highlighted_text end if type == 'match' || type.nil? diff --git a/app/views/projects/diffs/_parallel_view.html.haml b/app/views/projects/diffs/_parallel_view.html.haml index c6a9d71e789..4ded4d2daad 100644 --- a/app/views/projects/diffs/_parallel_view.html.haml +++ b/app/views/projects/diffs/_parallel_view.html.haml @@ -20,8 +20,7 @@ = link_to raw(line_number_left), "##{line_code_left}", id: line_code_left - if @comments_allowed && can?(current_user, :create_note, @project) = link_to_new_diff_note(line_code_left, 'old') - %td.line_content{class: "parallel noteable_line #{type_left} #{line_code_left}", "line_code" => line_code_left }< - = highlight_line(diff_file.new_path, unescape_html(line_content_left)) + %td.line_content{class: "parallel noteable_line #{type_left} #{line_code_left}", "line_code" => line_code_left }= raw(line_content_left) - if type_right == 'new' - new_line_class = 'new' @@ -34,8 +33,7 @@ = link_to raw(line_number_right), "##{new_line_code}", id: new_line_code - if @comments_allowed && can?(current_user, :create_note, @project) = link_to_new_diff_note(line_code_right, 'new') - %td.line_content.parallel{class: "noteable_line #{new_line_class} #{new_line_code}", "line_code" => new_line_code}< - = highlight_line(diff_file.new_path, unescape_html(line_content_right)) + %td.line_content.parallel{class: "noteable_line #{new_line_class} #{new_line_code}", "line_code" => new_line_code}= raw(line_content_right) - if @reply_allowed - comments_left, comments_right = organize_comments(type_left, type_right, line_code_left, line_code_right) diff --git a/app/views/projects/diffs/_text_file.html.haml b/app/views/projects/diffs/_text_file.html.haml index 78c66a6291e..641e9e5501a 100644 --- a/app/views/projects/diffs/_text_file.html.haml +++ b/app/views/projects/diffs/_text_file.html.haml @@ -6,7 +6,7 @@ %table.text-file.code.js-syntax-highlight{ class: [user_color_scheme, too_big ? 'hide' : ''] } - last_line = 0 - - diff_file.diff_lines.each_with_index do |line, index| + - diff_file.highlighted_diff_lines.each_with_index do |line, index| - type = line.type - last_line = line.new_pos - line_code = generate_line_code(diff_file.file_path, line) @@ -22,8 +22,7 @@ = link_to_new_diff_note(line_code) %td.new_line{data: {linenumber: line.new_pos}} = link_to raw(type == "old" ? " " : line.new_pos) , "##{line_code}", id: line_code - %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}< - = highlight_line(diff_file.new_path, unescape_html(diff_line_content(line.text))) + %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw(diff_line_content(line.highlighted_text)) - if @reply_allowed - comments = @line_notes.select { |n| n.line_code == line_code && n.active? }.sort_by(&:created_at) 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 /(?:(?!puts 'Hello' world) - end - - it 'should respect the inline diff markup' do - result = highlight_line('demo.rb', "puts 'Hello' world") - expect(result).to eq(expected) - end - end end -- GitLab