From ec0525d4975b9cae9982695cb0a19ac9c4bd1a3c Mon Sep 17 00:00:00 2001 From: Jean-Philippe Lang Date: Sun, 8 Jun 2008 16:28:42 +0000 Subject: [PATCH] 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 --- app/controllers/repositories_controller.rb | 2 +- app/models/repository.rb | 4 +- app/models/repository/cvs.rb | 4 +- app/models/repository/darcs.rb | 4 +- app/views/repositories/diff.rhtml | 2 +- lib/redmine/scm/adapters/abstract_adapter.rb | 161 +--------------------- lib/redmine/scm/adapters/bazaar_adapter.rb | 4 +- lib/redmine/scm/adapters/cvs_adapter.rb | 4 +- lib/redmine/scm/adapters/darcs_adapter.rb | 4 +- lib/redmine/scm/adapters/git_adapter.rb | 4 +- lib/redmine/scm/adapters/mercurial_adapter.rb | 4 +- lib/redmine/scm/adapters/subversion_adapter.rb | 2 +- lib/redmine/unified_diff.rb | 178 +++++++++++++++++++++++++ 13 files changed, 198 insertions(+), 179 deletions(-) create mode 100644 lib/redmine/unified_diff.rb diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb index ea3b117d..5fb2fdd7 100644 --- a/app/controllers/repositories_controller.rb +++ b/app/controllers/repositories_controller.rb @@ -153,7 +153,7 @@ class RepositoriesController < ApplicationController @cache_key = "repositories/diff/#{@repository.id}/" + Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}") 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 end rescue Redmine::Scm::Adapters::CommandFailed => e diff --git a/app/models/repository.rb b/app/models/repository.rb index e6ed7da5..1cf61393 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -55,8 +55,8 @@ class Repository < ActiveRecord::Base scm.entries(path, identifier) end - def diff(path, rev, rev_to, type) - scm.diff(path, rev, rev_to, type) + def diff(path, rev, rev_to) + scm.diff(path, rev, rev_to) end # Default behaviour: we search in cached changesets diff --git a/app/models/repository/cvs.rb b/app/models/repository/cvs.rb index c2d8be97..ea75de5d 100644 --- a/app/models/repository/cvs.rb +++ b/app/models/repository/cvs.rb @@ -53,7 +53,7 @@ class Repository::Cvs < Repository entries end - def diff(path, rev, rev_to, type) + def diff(path, rev, rev_to) #convert rev to revision. CVS can't handle changesets here diff=[] changeset_from=changesets.find_by_revision(rev) @@ -76,7 +76,7 @@ class Repository::Cvs < Repository unless revision_to revision_to=scm.get_previous_revision(revision_from) 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 return diff diff --git a/app/models/repository/darcs.rb b/app/models/repository/darcs.rb index c7c14a39..034c0c14 100644 --- a/app/models/repository/darcs.rb +++ b/app/models/repository/darcs.rb @@ -46,14 +46,14 @@ class Repository::Darcs < Repository entries end - def diff(path, rev, rev_to, type) + def diff(path, rev, rev_to) patch_from = changesets.find_by_revision(rev) return nil if patch_from.nil? patch_to = changesets.find_by_revision(rev_to) if rev_to if path.blank? path = patch_from.changes.collect{|change| change.path}.join(' ') 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 def fetch_changesets diff --git a/app/views/repositories/diff.rhtml b/app/views/repositories/diff.rhtml index 73c1e8c9..953c7c02 100644 --- a/app/views/repositories/diff.rhtml +++ b/app/views/repositories/diff.rhtml @@ -12,7 +12,7 @@ <% end %> <% cache(@cache_key) do -%> -<% @diff.each do |table_file| -%> +<% Redmine::UnifiedDiff.new(@diff, @diff_type).each do |table_file| -%>
<% if @diff_type == 'sbs' -%> diff --git a/lib/redmine/scm/adapters/abstract_adapter.rb b/lib/redmine/scm/adapters/abstract_adapter.rb index bd77ce20..0bacda77 100644 --- a/lib/redmine/scm/adapters/abstract_adapter.rb +++ b/lib/redmine/scm/adapters/abstract_adapter.rb @@ -82,7 +82,7 @@ module Redmine return nil end - def diff(path, identifier_from, identifier_to=nil, type="inline") + def diff(path, identifier_from, identifier_to=nil) return nil end @@ -234,166 +234,7 @@ module Redmine 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 attr_reader :lines, :revisions diff --git a/lib/redmine/scm/adapters/bazaar_adapter.rb b/lib/redmine/scm/adapters/bazaar_adapter.rb index 2225a627..ff69e3e6 100644 --- a/lib/redmine/scm/adapters/bazaar_adapter.rb +++ b/lib/redmine/scm/adapters/bazaar_adapter.rb @@ -132,7 +132,7 @@ module Redmine revisions end - def diff(path, identifier_from, identifier_to=nil, type="inline") + def diff(path, identifier_from, identifier_to=nil) path ||= '' if identifier_to identifier_to = identifier_to.to_i @@ -147,7 +147,7 @@ module Redmine end end #return nil if $? && $?.exitstatus != 0 - DiffTableList.new diff, type + diff end def cat(path, identifier=nil) diff --git a/lib/redmine/scm/adapters/cvs_adapter.rb b/lib/redmine/scm/adapters/cvs_adapter.rb index 37920b59..c86b02cb 100644 --- a/lib/redmine/scm/adapters/cvs_adapter.rb +++ b/lib/redmine/scm/adapters/cvs_adapter.rb @@ -227,7 +227,7 @@ module Redmine end end - def diff(path, identifier_from, identifier_to=nil, type="inline") + def diff(path, identifier_from, identifier_to=nil) logger.debug " diff path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}" 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}" @@ -238,7 +238,7 @@ module Redmine end end return nil if $? && $?.exitstatus != 0 - DiffTableList.new diff, type + diff end def cat(path, identifier=nil) diff --git a/lib/redmine/scm/adapters/darcs_adapter.rb b/lib/redmine/scm/adapters/darcs_adapter.rb index a1d1867b..b1b2a457 100644 --- a/lib/redmine/scm/adapters/darcs_adapter.rb +++ b/lib/redmine/scm/adapters/darcs_adapter.rb @@ -94,7 +94,7 @@ module Redmine revisions end - def diff(path, identifier_from, identifier_to=nil, type="inline") + def diff(path, identifier_from, identifier_to=nil) path = '*' if path.blank? cmd = "#{DARCS_BIN} diff --repodir #{@url}" if identifier_to.nil? @@ -111,7 +111,7 @@ module Redmine end end return nil if $? && $?.exitstatus != 0 - DiffTableList.new diff, type + diff end private diff --git a/lib/redmine/scm/adapters/git_adapter.rb b/lib/redmine/scm/adapters/git_adapter.rb index 9dfbd17a..d05b4fb3 100644 --- a/lib/redmine/scm/adapters/git_adapter.rb +++ b/lib/redmine/scm/adapters/git_adapter.rb @@ -204,7 +204,7 @@ module Redmine revisions end - def diff(path, identifier_from, identifier_to=nil, type="inline") + def diff(path, identifier_from, identifier_to=nil) path ||= '' if !identifier_to identifier_to = nil @@ -220,7 +220,7 @@ module Redmine end end return nil if $? && $?.exitstatus != 0 - DiffTableList.new diff, type + diff end def annotate(path, identifier=nil) diff --git a/lib/redmine/scm/adapters/mercurial_adapter.rb b/lib/redmine/scm/adapters/mercurial_adapter.rb index be01b7bb..28b842c2 100644 --- a/lib/redmine/scm/adapters/mercurial_adapter.rb +++ b/lib/redmine/scm/adapters/mercurial_adapter.rb @@ -117,7 +117,7 @@ module Redmine revisions end - def diff(path, identifier_from, identifier_to=nil, type="inline") + def diff(path, identifier_from, identifier_to=nil) path ||= '' if identifier_to identifier_to = identifier_to.to_i @@ -133,7 +133,7 @@ module Redmine end end return nil if $? && $?.exitstatus != 0 - DiffTableList.new diff, type + diff end def cat(path, identifier=nil) diff --git a/lib/redmine/scm/adapters/subversion_adapter.rb b/lib/redmine/scm/adapters/subversion_adapter.rb index 1cbdce13..7c98eee8 100644 --- a/lib/redmine/scm/adapters/subversion_adapter.rb +++ b/lib/redmine/scm/adapters/subversion_adapter.rb @@ -142,7 +142,7 @@ module Redmine end end return nil if $? && $?.exitstatus != 0 - DiffTableList.new diff, type + diff end def cat(path, identifier=nil) diff --git a/lib/redmine/unified_diff.rb b/lib/redmine/unified_diff.rb new file mode 100644 index 00000000..aa899445 --- /dev/null +++ b/lib/redmine/unified_diff.rb @@ -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 -- 2.11.0