Move unified diff parser out of the scm abstract adapter so it can be reused for viewing attached diffs (#1403).

git-svn-id: http://redmine.rubyforge.org/svn/trunk@1513 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
Jean-Philippe Lang 2008-06-08 16:28:42 +00:00
parent 731d15fe9b
commit ec0525d497
13 changed files with 198 additions and 179 deletions

View File

@ -153,7 +153,7 @@ class RepositoriesController < ApplicationController
@cache_key = "repositories/diff/#{@repository.id}/" + Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}") @cache_key = "repositories/diff/#{@repository.id}/" + Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}")
unless read_fragment(@cache_key) unless read_fragment(@cache_key)
@diff = @repository.diff(@path, @rev, @rev_to, @diff_type) @diff = @repository.diff(@path, @rev, @rev_to)
show_error_not_found unless @diff show_error_not_found unless @diff
end end
rescue Redmine::Scm::Adapters::CommandFailed => e rescue Redmine::Scm::Adapters::CommandFailed => e

View File

@ -55,8 +55,8 @@ class Repository < ActiveRecord::Base
scm.entries(path, identifier) scm.entries(path, identifier)
end end
def diff(path, rev, rev_to, type) def diff(path, rev, rev_to)
scm.diff(path, rev, rev_to, type) scm.diff(path, rev, rev_to)
end end
# Default behaviour: we search in cached changesets # Default behaviour: we search in cached changesets

View File

@ -53,7 +53,7 @@ class Repository::Cvs < Repository
entries entries
end end
def diff(path, rev, rev_to, type) def diff(path, rev, rev_to)
#convert rev to revision. CVS can't handle changesets here #convert rev to revision. CVS can't handle changesets here
diff=[] diff=[]
changeset_from=changesets.find_by_revision(rev) changeset_from=changesets.find_by_revision(rev)
@ -76,7 +76,7 @@ class Repository::Cvs < Repository
unless revision_to unless revision_to
revision_to=scm.get_previous_revision(revision_from) revision_to=scm.get_previous_revision(revision_from)
end end
diff=diff+scm.diff(change_from.path, revision_from, revision_to, type) diff=diff+scm.diff(change_from.path, revision_from, revision_to)
end end
end end
return diff return diff

View File

@ -46,14 +46,14 @@ class Repository::Darcs < Repository
entries entries
end end
def diff(path, rev, rev_to, type) def diff(path, rev, rev_to)
patch_from = changesets.find_by_revision(rev) patch_from = changesets.find_by_revision(rev)
return nil if patch_from.nil? return nil if patch_from.nil?
patch_to = changesets.find_by_revision(rev_to) if rev_to patch_to = changesets.find_by_revision(rev_to) if rev_to
if path.blank? if path.blank?
path = patch_from.changes.collect{|change| change.path}.join(' ') path = patch_from.changes.collect{|change| change.path}.join(' ')
end end
patch_from ? scm.diff(path, patch_from.scmid, patch_to ? patch_to.scmid : nil, type) : nil patch_from ? scm.diff(path, patch_from.scmid, patch_to ? patch_to.scmid : nil) : nil
end end
def fetch_changesets def fetch_changesets

View File

@ -12,7 +12,7 @@
<% end %> <% end %>
<% cache(@cache_key) do -%> <% cache(@cache_key) do -%>
<% @diff.each do |table_file| -%> <% Redmine::UnifiedDiff.new(@diff, @diff_type).each do |table_file| -%>
<div class="autoscroll"> <div class="autoscroll">
<% if @diff_type == 'sbs' -%> <% if @diff_type == 'sbs' -%>
<table class="filecontent CodeRay"> <table class="filecontent CodeRay">

View File

@ -82,7 +82,7 @@ module Redmine
return nil return nil
end end
def diff(path, identifier_from, identifier_to=nil, type="inline") def diff(path, identifier_from, identifier_to=nil)
return nil return nil
end end
@ -234,166 +234,7 @@ module Redmine
end end
end end
# A line of Diff
class Diff
attr_accessor :nb_line_left
attr_accessor :line_left
attr_accessor :nb_line_right
attr_accessor :line_right
attr_accessor :type_diff_right
attr_accessor :type_diff_left
def initialize ()
self.nb_line_left = ''
self.nb_line_right = ''
self.line_left = ''
self.line_right = ''
self.type_diff_right = ''
self.type_diff_left = ''
end
def inspect
puts '### Start Line Diff ###'
puts self.nb_line_left
puts self.line_left
puts self.nb_line_right
puts self.line_right
end
end
class DiffTableList < Array
def initialize (diff, type="inline")
diff_table = DiffTable.new type
diff.each do |line|
if line =~ /^(---|\+\+\+) (.*)$/
self << diff_table if diff_table.length > 1
diff_table = DiffTable.new type
end
a = diff_table.add_line line
end
self << diff_table unless diff_table.empty?
self
end
end
# Class for create a Diff
class DiffTable < Hash
attr_reader :file_name, :line_num_l, :line_num_r
# Initialize with a Diff file and the type of Diff View
# The type view must be inline or sbs (side_by_side)
def initialize(type="inline")
@parsing = false
@nb_line = 1
@start = false
@before = 'same'
@second = true
@type = type
end
# Function for add a line of this Diff
def add_line(line)
unless @parsing
if line =~ /^(---|\+\+\+) (.*)$/
@file_name = $2
return false
elsif line =~ /^@@ (\+|\-)(\d+)(,\d+)? (\+|\-)(\d+)(,\d+)? @@/
@line_num_l = $5.to_i
@line_num_r = $2.to_i
@parsing = true
end
else
if line =~ /^[^\+\-\s@\\]/
@parsing = false
return false
elsif line =~ /^@@ (\+|\-)(\d+)(,\d+)? (\+|\-)(\d+)(,\d+)? @@/
@line_num_l = $5.to_i
@line_num_r = $2.to_i
else
@nb_line += 1 if parse_line(line, @type)
end
end
return true
end
def inspect
puts '### DIFF TABLE ###'
puts "file : #{file_name}"
self.each do |d|
d.inspect
end
end
private
# Test if is a Side By Side type
def sbs?(type, func)
if @start and type == "sbs"
if @before == func and @second
tmp_nb_line = @nb_line
self[tmp_nb_line] = Diff.new
else
@second = false
tmp_nb_line = @start
@start += 1
@nb_line -= 1
end
else
tmp_nb_line = @nb_line
@start = @nb_line
self[tmp_nb_line] = Diff.new
@second = true
end
unless self[tmp_nb_line]
@nb_line += 1
self[tmp_nb_line] = Diff.new
else
self[tmp_nb_line]
end
end
# Escape the HTML for the diff
def escapeHTML(line)
CGI.escapeHTML(line)
end
def parse_line(line, type="inline")
if line[0, 1] == "+"
diff = sbs? type, 'add'
@before = 'add'
diff.line_left = escapeHTML line[1..-1]
diff.nb_line_left = @line_num_l
diff.type_diff_left = 'diff_in'
@line_num_l += 1
true
elsif line[0, 1] == "-"
diff = sbs? type, 'remove'
@before = 'remove'
diff.line_right = escapeHTML line[1..-1]
diff.nb_line_right = @line_num_r
diff.type_diff_right = 'diff_out'
@line_num_r += 1
true
elsif line[0, 1] =~ /\s/
@before = 'same'
@start = false
diff = Diff.new
diff.line_right = escapeHTML line[1..-1]
diff.nb_line_right = @line_num_r
diff.line_left = escapeHTML line[1..-1]
diff.nb_line_left = @line_num_l
self[@nb_line] = diff
@line_num_l += 1
@line_num_r += 1
true
elsif line[0, 1] = "\\"
true
else
false
end
end
end
class Annotate class Annotate
attr_reader :lines, :revisions attr_reader :lines, :revisions

View File

@ -132,7 +132,7 @@ module Redmine
revisions revisions
end end
def diff(path, identifier_from, identifier_to=nil, type="inline") def diff(path, identifier_from, identifier_to=nil)
path ||= '' path ||= ''
if identifier_to if identifier_to
identifier_to = identifier_to.to_i identifier_to = identifier_to.to_i
@ -147,7 +147,7 @@ module Redmine
end end
end end
#return nil if $? && $?.exitstatus != 0 #return nil if $? && $?.exitstatus != 0
DiffTableList.new diff, type diff
end end
def cat(path, identifier=nil) def cat(path, identifier=nil)

View File

@ -227,7 +227,7 @@ module Redmine
end end
end end
def diff(path, identifier_from, identifier_to=nil, type="inline") def diff(path, identifier_from, identifier_to=nil)
logger.debug "<cvs> diff path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}" logger.debug "<cvs> diff path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
path_with_project="#{url}#{with_leading_slash(path)}" path_with_project="#{url}#{with_leading_slash(path)}"
cmd = "#{CVS_BIN} -d #{root_url} rdiff -u -r#{identifier_to} -r#{identifier_from} #{path_with_project}" cmd = "#{CVS_BIN} -d #{root_url} rdiff -u -r#{identifier_to} -r#{identifier_from} #{path_with_project}"
@ -238,7 +238,7 @@ module Redmine
end end
end end
return nil if $? && $?.exitstatus != 0 return nil if $? && $?.exitstatus != 0
DiffTableList.new diff, type diff
end end
def cat(path, identifier=nil) def cat(path, identifier=nil)

View File

@ -94,7 +94,7 @@ module Redmine
revisions revisions
end end
def diff(path, identifier_from, identifier_to=nil, type="inline") def diff(path, identifier_from, identifier_to=nil)
path = '*' if path.blank? path = '*' if path.blank?
cmd = "#{DARCS_BIN} diff --repodir #{@url}" cmd = "#{DARCS_BIN} diff --repodir #{@url}"
if identifier_to.nil? if identifier_to.nil?
@ -111,7 +111,7 @@ module Redmine
end end
end end
return nil if $? && $?.exitstatus != 0 return nil if $? && $?.exitstatus != 0
DiffTableList.new diff, type diff
end end
private private

View File

@ -204,7 +204,7 @@ module Redmine
revisions revisions
end end
def diff(path, identifier_from, identifier_to=nil, type="inline") def diff(path, identifier_from, identifier_to=nil)
path ||= '' path ||= ''
if !identifier_to if !identifier_to
identifier_to = nil identifier_to = nil
@ -220,7 +220,7 @@ module Redmine
end end
end end
return nil if $? && $?.exitstatus != 0 return nil if $? && $?.exitstatus != 0
DiffTableList.new diff, type diff
end end
def annotate(path, identifier=nil) def annotate(path, identifier=nil)

View File

@ -117,7 +117,7 @@ module Redmine
revisions revisions
end end
def diff(path, identifier_from, identifier_to=nil, type="inline") def diff(path, identifier_from, identifier_to=nil)
path ||= '' path ||= ''
if identifier_to if identifier_to
identifier_to = identifier_to.to_i identifier_to = identifier_to.to_i
@ -133,7 +133,7 @@ module Redmine
end end
end end
return nil if $? && $?.exitstatus != 0 return nil if $? && $?.exitstatus != 0
DiffTableList.new diff, type diff
end end
def cat(path, identifier=nil) def cat(path, identifier=nil)

View File

@ -142,7 +142,7 @@ module Redmine
end end
end end
return nil if $? && $?.exitstatus != 0 return nil if $? && $?.exitstatus != 0
DiffTableList.new diff, type diff
end end
def cat(path, identifier=nil) def cat(path, identifier=nil)

178
lib/redmine/unified_diff.rb Normal file
View File

@ -0,0 +1,178 @@
# redMine - project management software
# Copyright (C) 2006-2008 Jean-Philippe Lang
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
module Redmine
# Class used to parse unified diffs
class UnifiedDiff < Array
def initialize(diff, type="inline")
diff_table = DiffTable.new type
diff.each do |line|
if line =~ /^(---|\+\+\+) (.*)$/
self << diff_table if diff_table.length > 1
diff_table = DiffTable.new type
end
a = diff_table.add_line line
end
self << diff_table unless diff_table.empty?
self
end
end
# Class that represents a file diff
class DiffTable < Hash
attr_reader :file_name, :line_num_l, :line_num_r
# Initialize with a Diff file and the type of Diff View
# The type view must be inline or sbs (side_by_side)
def initialize(type="inline")
@parsing = false
@nb_line = 1
@start = false
@before = 'same'
@second = true
@type = type
end
# Function for add a line of this Diff
def add_line(line)
unless @parsing
if line =~ /^(---|\+\+\+) (.*)$/
@file_name = $2
return false
elsif line =~ /^@@ (\+|\-)(\d+)(,\d+)? (\+|\-)(\d+)(,\d+)? @@/
@line_num_l = $5.to_i
@line_num_r = $2.to_i
@parsing = true
end
else
if line =~ /^[^\+\-\s@\\]/
@parsing = false
return false
elsif line =~ /^@@ (\+|\-)(\d+)(,\d+)? (\+|\-)(\d+)(,\d+)? @@/
@line_num_l = $5.to_i
@line_num_r = $2.to_i
else
@nb_line += 1 if parse_line(line, @type)
end
end
return true
end
def inspect
puts '### DIFF TABLE ###'
puts "file : #{file_name}"
self.each do |d|
d.inspect
end
end
private
# Test if is a Side By Side type
def sbs?(type, func)
if @start and type == "sbs"
if @before == func and @second
tmp_nb_line = @nb_line
self[tmp_nb_line] = Diff.new
else
@second = false
tmp_nb_line = @start
@start += 1
@nb_line -= 1
end
else
tmp_nb_line = @nb_line
@start = @nb_line
self[tmp_nb_line] = Diff.new
@second = true
end
unless self[tmp_nb_line]
@nb_line += 1
self[tmp_nb_line] = Diff.new
else
self[tmp_nb_line]
end
end
# Escape the HTML for the diff
def escapeHTML(line)
CGI.escapeHTML(line)
end
def parse_line(line, type="inline")
if line[0, 1] == "+"
diff = sbs? type, 'add'
@before = 'add'
diff.line_left = escapeHTML line[1..-1]
diff.nb_line_left = @line_num_l
diff.type_diff_left = 'diff_in'
@line_num_l += 1
true
elsif line[0, 1] == "-"
diff = sbs? type, 'remove'
@before = 'remove'
diff.line_right = escapeHTML line[1..-1]
diff.nb_line_right = @line_num_r
diff.type_diff_right = 'diff_out'
@line_num_r += 1
true
elsif line[0, 1] =~ /\s/
@before = 'same'
@start = false
diff = Diff.new
diff.line_right = escapeHTML line[1..-1]
diff.nb_line_right = @line_num_r
diff.line_left = escapeHTML line[1..-1]
diff.nb_line_left = @line_num_l
self[@nb_line] = diff
@line_num_l += 1
@line_num_r += 1
true
elsif line[0, 1] = "\\"
true
else
false
end
end
end
# A line of diff
class Diff
attr_accessor :nb_line_left
attr_accessor :line_left
attr_accessor :nb_line_right
attr_accessor :line_right
attr_accessor :type_diff_right
attr_accessor :type_diff_left
def initialize()
self.nb_line_left = ''
self.nb_line_right = ''
self.line_left = ''
self.line_right = ''
self.type_diff_right = ''
self.type_diff_left = ''
end
def inspect
puts '### Start Line Diff ###'
puts self.nb_line_left
puts self.line_left
puts self.nb_line_right
puts self.line_right
end
end
end