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.
This commit is contained in:
parent
c031b9d9cd
commit
bb96d63153
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -11,7 +11,7 @@ module Rouge
|
|||
token InlineDiff, match[1]
|
||||
end
|
||||
|
||||
rule /(?:(?!<span).)*/ do
|
||||
rule /(?:(?!<span class='idiff').)*/m do
|
||||
delegate option(:parent_lexer)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -64,15 +64,4 @@ describe BlobHelper do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'highlight_line' do
|
||||
let(:expected) do
|
||||
%q(<span id="LC1" class="line"><span class="nb">puts</span> <span class="s1">'Hello'</span> <span class="idiff">world</span></span>)
|
||||
end
|
||||
|
||||
it 'should respect the inline diff markup' do
|
||||
result = highlight_line('demo.rb', "puts 'Hello' <span class='idiff'>world</span>")
|
||||
expect(result).to eq(expected)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue