Initial Revision
[ohcount] / lib / ohcount / diff.rb
1 module Ohcount
2
3         def self.diff_files(old_filename, new_filename)
4                 langs = {}
5                 ScratchDir.new do |old_dir|
6                         ScratchDir.new do |new_dir|
7                                 old_buffer = File.new(old_filename).read
8                                 old_polyglot = Ohcount::Detector.detect(SimpleFileContext.new(old_filename))
9                                 Ohcount::Parser.parse_to_dir(:dir => old_dir,
10                                                                                                                                                 :buffer => old_buffer,
11                                                                                                                                                 :polyglot => old_polyglot)
12                                 new_buffer = File.new(new_filename).read
13                                 new_polyglot = Ohcount::Detector.detect(SimpleFileContext.new(new_filename))
14                                 Ohcount::Parser.parse_to_dir(:dir => new_dir,
15                                                                                                                                                 :buffer => new_buffer,
16                                                                                                                                                 :polyglot => new_polyglot)
17                                 langs = diff_sloc_dirs(old_dir, new_dir)
18                         end
19                 end
20                 langs
21         end
22
23         def self.blank_diffs(new_file, old_file)
24                 # blanks are stored as a simple string
25                 old_blanks = new_blanks = 0
26                 if old_file != "/dev/null" && FileTest.file?(old_file)
27                         File.open(old_file) do |io|
28                         old_blanks = io.read.to_i
29                         end
30                 end
31                 if new_file != "/dev/null" && FileTest.file?(new_file)
32                         File.open(new_file) do |io|
33                         new_blanks = io.read.to_i
34                         end
35                 end
36                 blanks_changed = new_blanks - old_blanks
37                 blanks_added = blanks_changed > 0 ? blanks_changed : 0
38                 blanks_removed = blanks_changed < 0 ? -blanks_changed : 0
39                 [blanks_added, blanks_removed]
40         end
41
42
43         def self.get_langs(dir)
44                 return {} if dir.nil?
45                 langs = {}
46                 Dir.foreach(dir) do |l|
47                         next if [".", ".."].include?(l)
48                         langs[l] ||= {}
49                         Dir.foreach(dir + "/" + l) do |s|
50                                 next if [".", ".."].include?(s)
51                                 langs[l][s] = nil
52                         end
53                 end
54                 langs
55         end
56
57         def self.diff_sloc_dirs(old_dir, new_dir)
58                 # figure out the superset of all languages and semantics we're dealing with (on old and new)
59                 langs = {}
60                 langs.merge!(get_langs(old_dir))
61                 langs.merge!(get_langs(new_dir))
62
63                 # now go through each and tally up the added/removed
64                 sloc_infos = []
65                 langs.each do |lang, semantics|
66       next if lang == "_file" # this is a special case...
67                         sloc_info = SlocInfo.new(lang)
68
69                         semantics.each_key do |s|
70                                 old_file = old_dir ? File.join(old_dir, lang, s) : '/dev/null'
71                                 new_file = new_dir ? File.join(new_dir, lang, s) : '/dev/null'
72                                 if s == 'blanks'
73                                         sloc_info.blanks_added, sloc_info.blanks_removed = blank_diffs(new_file, old_file)
74                                 else
75                                         added_removed = diff_file(old_file, new_file)
76                                         case s
77                                         when 'code'
78                                                 sloc_info.code_added, sloc_info.code_removed = added_removed
79                                         when 'comment'
80                                                 sloc_info.comments_added, sloc_info.comments_removed = added_removed
81                                         else
82                                                 raise RuntimeError.new("unknown semantic: #{ s }")
83                                         end
84                                 end
85                         end
86                         sloc_infos << sloc_info unless sloc_info.empty?
87                 end
88
89                 # sort them -- nice thing to do
90                 sloc_infos.sort do |a,b|
91                         a.language <=> b.language
92                 end
93         end
94
95         def self.diff_file(old, new)
96                 added = removed = 0
97
98                 if FileTest.file?(old) == false
99                         # original file doesn't exist -> return all new lines as added
100                         added = `cat '#{ new }' | wc -l`.strip.to_i
101                 elsif FileTest.file?(new) == false
102                         # new file doesn't exist -> return all old lines as deleted
103                         removed = `cat '#{ old }' | wc -l`.strip.to_i
104                 else
105                         cmd = "diff -d --normal --suppress-common-lines --new-file '#{old}' '#{new}' | grep '^>' | wc -l"
106                         added = `#{cmd}`.to_i
107                         cmd = "diff -d --normal --suppress-common-lines --new-file '#{old}' '#{new}' | grep '^<' | wc -l"
108                         removed = `#{cmd}`.to_i
109                 end
110                 [added, removed]
111         end
112 end