glib2 0.20.0 → 0.90.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (175) hide show
  1. data/ChangeLog +92 -0
  2. data/README +22 -10
  3. data/Rakefile +54 -72
  4. data/ext/glib2/depend +10 -0
  5. data/ext/glib2/extconf.rb +61 -0
  6. data/ext/glib2/glib2.def +89 -0
  7. data/{src → ext/glib2}/rbgcompat.h +0 -0
  8. data/{src → ext/glib2}/rbglib.c +17 -7
  9. data/{src → ext/glib2}/rbglib.h +2 -2
  10. data/{src → ext/glib2}/rbglib_bookmarkfile.c +0 -0
  11. data/{src → ext/glib2}/rbglib_completion.c +0 -0
  12. data/{src → ext/glib2}/rbglib_convert.c +0 -0
  13. data/{src → ext/glib2}/rbglib_error.c +0 -0
  14. data/{src → ext/glib2}/rbglib_fileutils.c +0 -0
  15. data/{src → ext/glib2}/rbglib_i18n.c +0 -0
  16. data/{src → ext/glib2}/rbglib_int64.c +0 -0
  17. data/{src → ext/glib2}/rbglib_iochannel.c +0 -0
  18. data/{src → ext/glib2}/rbglib_keyfile.c +0 -0
  19. data/{src → ext/glib2}/rbglib_maincontext.c +1 -3
  20. data/{src → ext/glib2}/rbglib_mainloop.c +0 -0
  21. data/{src → ext/glib2}/rbglib_messages.c +0 -0
  22. data/{src → ext/glib2}/rbglib_pollfd.c +0 -0
  23. data/{src → ext/glib2}/rbglib_shell.c +0 -0
  24. data/{src → ext/glib2}/rbglib_source.c +0 -0
  25. data/{src → ext/glib2}/rbglib_spawn.c +0 -0
  26. data/{src → ext/glib2}/rbglib_threads.c +0 -0
  27. data/{src → ext/glib2}/rbglib_timer.c +0 -0
  28. data/{src → ext/glib2}/rbglib_unicode.c +0 -0
  29. data/{src → ext/glib2}/rbglib_utils.c +0 -0
  30. data/{src → ext/glib2}/rbglib_win32.c +0 -0
  31. data/{src → ext/glib2}/rbgobj_boxed.c +0 -0
  32. data/{src → ext/glib2}/rbgobj_closure.c +0 -0
  33. data/{src → ext/glib2}/rbgobj_convert.c +0 -0
  34. data/{src → ext/glib2}/rbgobj_enums.c +0 -0
  35. data/{src → ext/glib2}/rbgobj_fundamental.c +0 -0
  36. data/{src → ext/glib2}/rbgobj_object.c +0 -0
  37. data/{src → ext/glib2}/rbgobj_param.c +0 -0
  38. data/{src → ext/glib2}/rbgobj_paramspecs.c +0 -0
  39. data/{src → ext/glib2}/rbgobj_signal.c +0 -0
  40. data/{src → ext/glib2}/rbgobj_strv.c +0 -0
  41. data/{src → ext/glib2}/rbgobj_type.c +0 -0
  42. data/{src → ext/glib2}/rbgobj_typeinstance.c +0 -0
  43. data/{src → ext/glib2}/rbgobj_typeinterface.c +0 -0
  44. data/{src → ext/glib2}/rbgobj_typemodule.c +0 -0
  45. data/{src → ext/glib2}/rbgobj_typeplugin.c +0 -0
  46. data/{src → ext/glib2}/rbgobj_value.c +0 -0
  47. data/{src → ext/glib2}/rbgobj_valuearray.c +0 -0
  48. data/{src → ext/glib2}/rbgobj_valuetypes.c +0 -0
  49. data/{src → ext/glib2}/rbgobject.c +0 -0
  50. data/{src → ext/glib2}/rbgobject.h +0 -0
  51. data/{src → ext/glib2}/rbgprivate.h +0 -0
  52. data/ext/glib2/rbgutil.c +316 -0
  53. data/{src → ext/glib2}/rbgutil.h +9 -9
  54. data/{src → ext/glib2}/rbgutil_callback.c +0 -0
  55. data/extconf.rb +48 -60
  56. data/{src/lib → lib}/glib-mkenums.rb +2 -2
  57. data/{src/lib → lib}/glib2.rb +28 -9
  58. data/lib/gnome2-win32-binary-downloader.rb +92 -0
  59. data/{src/lib → lib}/mkmf-gnome2.rb +76 -89
  60. data/test-unit/COPYING +56 -0
  61. data/test-unit/GPL +340 -0
  62. data/test-unit/History.txt +232 -0
  63. data/test-unit/Manifest.txt +110 -0
  64. data/test-unit/PSFL +271 -0
  65. data/test-unit/README.txt +75 -0
  66. data/test-unit/Rakefile +53 -0
  67. data/test-unit/TODO +5 -0
  68. data/test-unit/bin/testrb +5 -0
  69. data/test-unit/html/bar.png +0 -0
  70. data/test-unit/html/bar.svg +153 -0
  71. data/test-unit/html/developer.png +0 -0
  72. data/test-unit/html/developer.svg +469 -0
  73. data/test-unit/html/famfamfam-logo.png +0 -0
  74. data/test-unit/html/favicon.ico +0 -0
  75. data/test-unit/html/favicon.png +0 -0
  76. data/test-unit/html/favicon.svg +82 -0
  77. data/test-unit/html/heading-mark.png +0 -0
  78. data/test-unit/html/heading-mark.svg +393 -0
  79. data/test-unit/html/index.html +247 -0
  80. data/test-unit/html/index.html.ja +270 -0
  81. data/test-unit/html/install.png +0 -0
  82. data/test-unit/html/install.svg +636 -0
  83. data/test-unit/html/jp.png +0 -0
  84. data/test-unit/html/kinotan-failure.png +0 -0
  85. data/test-unit/html/kinotan-pass.png +0 -0
  86. data/test-unit/html/logo.png +0 -0
  87. data/test-unit/html/logo.svg +483 -0
  88. data/test-unit/html/reference.png +0 -0
  89. data/test-unit/html/rubyforge.png +0 -0
  90. data/test-unit/html/tango-logo.png +0 -0
  91. data/test-unit/html/test-unit.css +339 -0
  92. data/test-unit/html/tutorial.png +0 -0
  93. data/test-unit/html/tutorial.svg +559 -0
  94. data/test-unit/html/us.png +0 -0
  95. data/test-unit/images/color-diff.png +0 -0
  96. data/test-unit/lib/test/unit.rb +328 -0
  97. data/test-unit/lib/test/unit/assertionfailederror.rb +25 -0
  98. data/test-unit/lib/test/unit/assertions.rb +1334 -0
  99. data/test-unit/lib/test/unit/attribute.rb +125 -0
  100. data/test-unit/lib/test/unit/autorunner.rb +363 -0
  101. data/test-unit/lib/test/unit/collector.rb +36 -0
  102. data/test-unit/lib/test/unit/collector/descendant.rb +23 -0
  103. data/test-unit/lib/test/unit/collector/dir.rb +108 -0
  104. data/test-unit/lib/test/unit/collector/load.rb +144 -0
  105. data/test-unit/lib/test/unit/collector/objectspace.rb +34 -0
  106. data/test-unit/lib/test/unit/color-scheme.rb +106 -0
  107. data/test-unit/lib/test/unit/color.rb +96 -0
  108. data/test-unit/lib/test/unit/diff.rb +740 -0
  109. data/test-unit/lib/test/unit/error.rb +130 -0
  110. data/test-unit/lib/test/unit/exceptionhandler.rb +39 -0
  111. data/test-unit/lib/test/unit/failure.rb +136 -0
  112. data/test-unit/lib/test/unit/fixture.rb +176 -0
  113. data/test-unit/lib/test/unit/notification.rb +129 -0
  114. data/test-unit/lib/test/unit/omission.rb +191 -0
  115. data/test-unit/lib/test/unit/pending.rb +150 -0
  116. data/test-unit/lib/test/unit/priority.rb +180 -0
  117. data/test-unit/lib/test/unit/runner/console.rb +52 -0
  118. data/test-unit/lib/test/unit/runner/emacs.rb +8 -0
  119. data/test-unit/lib/test/unit/runner/tap.rb +8 -0
  120. data/test-unit/lib/test/unit/testcase.rb +483 -0
  121. data/test-unit/lib/test/unit/testresult.rb +121 -0
  122. data/test-unit/lib/test/unit/testsuite.rb +110 -0
  123. data/test-unit/lib/test/unit/ui/console/outputlevel.rb +14 -0
  124. data/test-unit/lib/test/unit/ui/console/testrunner.rb +430 -0
  125. data/test-unit/lib/test/unit/ui/emacs/testrunner.rb +63 -0
  126. data/test-unit/lib/test/unit/ui/tap/testrunner.rb +82 -0
  127. data/test-unit/lib/test/unit/ui/testrunner.rb +53 -0
  128. data/test-unit/lib/test/unit/ui/testrunnermediator.rb +77 -0
  129. data/test-unit/lib/test/unit/ui/testrunnerutilities.rb +41 -0
  130. data/test-unit/lib/test/unit/util/backtracefilter.rb +42 -0
  131. data/test-unit/lib/test/unit/util/method-owner-finder.rb +28 -0
  132. data/test-unit/lib/test/unit/util/observable.rb +90 -0
  133. data/test-unit/lib/test/unit/util/output.rb +31 -0
  134. data/test-unit/lib/test/unit/util/procwrapper.rb +48 -0
  135. data/test-unit/lib/test/unit/version.rb +7 -0
  136. data/test-unit/sample/adder.rb +13 -0
  137. data/test-unit/sample/subtracter.rb +12 -0
  138. data/test-unit/sample/test_adder.rb +20 -0
  139. data/test-unit/sample/test_subtracter.rb +20 -0
  140. data/test-unit/sample/test_user.rb +23 -0
  141. data/test-unit/test/collector/test-descendant.rb +133 -0
  142. data/test-unit/test/collector/test-load.rb +442 -0
  143. data/test-unit/test/collector/test_dir.rb +406 -0
  144. data/test-unit/test/collector/test_objectspace.rb +100 -0
  145. data/test-unit/test/run-test.rb +15 -0
  146. data/test-unit/test/test-attribute.rb +86 -0
  147. data/test-unit/test/test-color-scheme.rb +69 -0
  148. data/test-unit/test/test-color.rb +47 -0
  149. data/test-unit/test/test-diff.rb +518 -0
  150. data/test-unit/test/test-emacs-runner.rb +60 -0
  151. data/test-unit/test/test-fixture.rb +287 -0
  152. data/test-unit/test/test-notification.rb +33 -0
  153. data/test-unit/test/test-omission.rb +81 -0
  154. data/test-unit/test/test-pending.rb +70 -0
  155. data/test-unit/test/test-priority.rb +119 -0
  156. data/test-unit/test/test-testcase.rb +544 -0
  157. data/test-unit/test/test_assertions.rb +1197 -0
  158. data/test-unit/test/test_error.rb +26 -0
  159. data/test-unit/test/test_failure.rb +33 -0
  160. data/test-unit/test/test_testresult.rb +113 -0
  161. data/test-unit/test/test_testsuite.rb +129 -0
  162. data/test-unit/test/testunit-test-util.rb +14 -0
  163. data/test-unit/test/ui/test_tap.rb +33 -0
  164. data/test-unit/test/ui/test_testrunmediator.rb +20 -0
  165. data/test-unit/test/util/test-method-owner-finder.rb +38 -0
  166. data/test-unit/test/util/test-output.rb +11 -0
  167. data/test-unit/test/util/test_backtracefilter.rb +41 -0
  168. data/test-unit/test/util/test_observable.rb +102 -0
  169. data/test-unit/test/util/test_procwrapper.rb +36 -0
  170. data/test/run-test.rb +2 -6
  171. metadata +197 -100
  172. data/src/glib-enum-types.c +0 -1032
  173. data/src/glib-enum-types.h +0 -140
  174. data/src/lib/pkg-config.rb +0 -137
  175. data/src/rbgutil.c +0 -222
@@ -0,0 +1,96 @@
1
+ module Test
2
+ module Unit
3
+ class Color
4
+ NAMES = ["black", "red", "green", "yellow",
5
+ "blue", "magenta", "cyan", "white"]
6
+
7
+ attr_reader :name
8
+ def initialize(name, options={})
9
+ @name = name
10
+ @foreground = options[:foreground]
11
+ @foreground = true if @foreground.nil?
12
+ @intensity = options[:intensity]
13
+ @bold = options[:bold]
14
+ @italic = options[:italic]
15
+ @underline = options[:underline]
16
+ end
17
+
18
+ def foreground?
19
+ @foreground
20
+ end
21
+
22
+ def intensity?
23
+ @intensity
24
+ end
25
+
26
+ def bold?
27
+ @bold
28
+ end
29
+
30
+ def italic?
31
+ @italic
32
+ end
33
+
34
+ def underline?
35
+ @underline
36
+ end
37
+
38
+ def ==(other)
39
+ self.class === other and
40
+ [name, foreground?, intensity?,
41
+ bold?, italic?, underline?] ==
42
+ [other.name, other.foreground?, other.intensity?,
43
+ other.bold?, other.italic?, other.underline?]
44
+ end
45
+
46
+ def sequence
47
+ sequence = []
48
+ if @name == "none"
49
+ elsif @name == "reset"
50
+ sequence << "0"
51
+ else
52
+ foreground_parameter = foreground? ? 3 : 4
53
+ foreground_parameter += 6 if intensity?
54
+ sequence << "#{foreground_parameter}#{NAMES.index(@name)}"
55
+ end
56
+ sequence << "1" if bold?
57
+ sequence << "3" if italic?
58
+ sequence << "4" if underline?
59
+ sequence
60
+ end
61
+
62
+ def escape_sequence
63
+ "\e[#{sequence.join(';')}m"
64
+ end
65
+
66
+ def +(other)
67
+ MixColor.new([self, other])
68
+ end
69
+ end
70
+
71
+ class MixColor
72
+ attr_reader :colors
73
+ def initialize(colors)
74
+ @colors = colors
75
+ end
76
+
77
+ def sequence
78
+ @colors.inject([]) do |result, color|
79
+ result + color.sequence
80
+ end
81
+ end
82
+
83
+ def escape_sequence
84
+ "\e[#{sequence.join(';')}m"
85
+ end
86
+
87
+ def +(other)
88
+ self.class.new([self, other])
89
+ end
90
+
91
+ def ==(other)
92
+ self.class === other and colors == other.colors
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,740 @@
1
+ # port of Python's difflib.
2
+ #
3
+ # Copyright (c) 2001-2008 Python Software Foundation; All Rights Reserved
4
+ # Copyright (c) 2008-2010 Kouhei Sutou; All Rights Reserved
5
+ #
6
+ # It is free software, and is distributed under the Ruby
7
+ # license and/or the PSF license. See the COPYING file and
8
+ # PSFL file.
9
+
10
+ module Test
11
+ module Unit
12
+ module Diff
13
+ class SequenceMatcher
14
+ def initialize(from, to, &junk_predicate)
15
+ @from = from
16
+ @to = to
17
+ @junk_predicate = junk_predicate
18
+ update_to_indexes
19
+ end
20
+
21
+ def longest_match(from_start, from_end, to_start, to_end)
22
+ best_info = find_best_match_position(from_start, from_end,
23
+ to_start, to_end)
24
+ unless @junks.empty?
25
+ args = [from_start, from_end, to_start, to_end]
26
+ best_info = adjust_best_info_with_junk_predicate(false, best_info,
27
+ *args)
28
+ best_info = adjust_best_info_with_junk_predicate(true, best_info,
29
+ *args)
30
+ end
31
+
32
+ best_info
33
+ end
34
+
35
+ def blocks
36
+ @blocks ||= compute_blocks
37
+ end
38
+
39
+ def operations
40
+ @operations ||= compute_operations
41
+ end
42
+
43
+ def grouped_operations(context_size=nil)
44
+ context_size ||= 3
45
+ _operations = operations.dup
46
+ _operations = [[:equal, 0, 0, 0, 0]] if _operations.empty?
47
+ expand_edge_equal_operations!(_operations, context_size)
48
+
49
+ group_window = context_size * 2
50
+ groups = []
51
+ group = []
52
+ _operations.each do |tag, from_start, from_end, to_start, to_end|
53
+ if tag == :equal and from_end - from_start > group_window
54
+ group << [tag,
55
+ from_start,
56
+ [from_end, from_start + context_size].min,
57
+ to_start,
58
+ [to_end, to_start + context_size].min]
59
+ groups << group
60
+ group = []
61
+ from_start = [from_start, from_end - context_size].max
62
+ to_start = [to_start, to_end - context_size].max
63
+ end
64
+ group << [tag, from_start, from_end, to_start, to_end]
65
+ end
66
+ groups << group unless group.empty?
67
+ groups
68
+ end
69
+
70
+ def ratio
71
+ @ratio ||= compute_ratio
72
+ end
73
+
74
+ private
75
+ def update_to_indexes
76
+ @to_indexes = {}
77
+ @junks = {}
78
+ if @to.is_a?(String)
79
+ each = " "[0].is_a?(Integer) ? :each_byte : :each_char
80
+ else
81
+ each = :each
82
+ end
83
+ i = 0
84
+ @to.send(each) do |item|
85
+ @to_indexes[item] ||= []
86
+ @to_indexes[item] << i
87
+ i += 1
88
+ end
89
+
90
+ return if @junk_predicate.nil?
91
+ @to_indexes = @to_indexes.reject do |key, value|
92
+ junk = @junk_predicate.call(key)
93
+ @junks[key] = true if junk
94
+ junk
95
+ end
96
+ end
97
+
98
+ def find_best_match_position(from_start, from_end, to_start, to_end)
99
+ best_from, best_to, best_size = from_start, to_start, 0
100
+ sizes = {}
101
+ from_start.upto(from_end) do |from_index|
102
+ _sizes = {}
103
+ (@to_indexes[@from[from_index]] || []).each do |to_index|
104
+ next if to_index < to_start
105
+ break if to_index > to_end
106
+ size = _sizes[to_index] = (sizes[to_index - 1] || 0) + 1
107
+ if size > best_size
108
+ best_from = from_index - size + 1
109
+ best_to = to_index - size + 1
110
+ best_size = size
111
+ end
112
+ end
113
+ sizes = _sizes
114
+ end
115
+ [best_from, best_to, best_size]
116
+ end
117
+
118
+ def adjust_best_info_with_junk_predicate(should_junk, best_info,
119
+ from_start, from_end,
120
+ to_start, to_end)
121
+ best_from, best_to, best_size = best_info
122
+ while best_from > from_start and best_to > to_start and
123
+ (should_junk ?
124
+ @junks.has_key?(@to[best_to - 1]) :
125
+ !@junks.has_key?(@to[best_to - 1])) and
126
+ @from[best_from - 1] == @to[best_to - 1]
127
+ best_from -= 1
128
+ best_to -= 1
129
+ best_size += 1
130
+ end
131
+
132
+ while best_from + best_size < from_end and
133
+ best_to + best_size < to_end and
134
+ (should_junk ?
135
+ @junks.has_key?(@to[best_to + best_size]) :
136
+ !@junks.has_key?(@to[best_to + best_size])) and
137
+ @from[best_from + best_size] == @to[best_to + best_size]
138
+ best_size += 1
139
+ end
140
+
141
+ [best_from, best_to, best_size]
142
+ end
143
+
144
+ def matches
145
+ @matches ||= compute_matches
146
+ end
147
+
148
+ def compute_matches
149
+ matches = []
150
+ queue = [[0, @from.size, 0, @to.size]]
151
+ until queue.empty?
152
+ from_start, from_end, to_start, to_end = queue.pop
153
+ match = longest_match(from_start, from_end - 1, to_start, to_end - 1)
154
+ match_from_index, match_to_index, size = match
155
+ unless size.zero?
156
+ if from_start < match_from_index and
157
+ to_start < match_to_index
158
+ queue.push([from_start, match_from_index,
159
+ to_start, match_to_index])
160
+ end
161
+ matches << match
162
+ if match_from_index + size < from_end and
163
+ match_to_index + size < to_end
164
+ queue.push([match_from_index + size, from_end,
165
+ match_to_index + size, to_end])
166
+ end
167
+ end
168
+ end
169
+ matches.sort_by do |(from_index, to_index, match_size)|
170
+ from_index
171
+ end
172
+ end
173
+
174
+ def compute_blocks
175
+ blocks = []
176
+ current_from_index = current_to_index = current_size = 0
177
+ matches.each do |from_index, to_index, size|
178
+ if current_from_index + current_size == from_index and
179
+ current_to_index + current_size == to_index
180
+ current_size += size
181
+ else
182
+ unless current_size.zero?
183
+ blocks << [current_from_index, current_to_index, current_size]
184
+ end
185
+ current_from_index = from_index
186
+ current_to_index = to_index
187
+ current_size = size
188
+ end
189
+ end
190
+ unless current_size.zero?
191
+ blocks << [current_from_index, current_to_index, current_size]
192
+ end
193
+
194
+ blocks << [@from.size, @to.size, 0]
195
+ blocks
196
+ end
197
+
198
+ def compute_operations
199
+ from_index = to_index = 0
200
+ operations = []
201
+ blocks.each do |match_from_index, match_to_index, size|
202
+ tag = determine_tag(from_index, to_index,
203
+ match_from_index, match_to_index)
204
+ if tag != :equal
205
+ operations << [tag,
206
+ from_index, match_from_index,
207
+ to_index, match_to_index]
208
+ end
209
+
210
+ from_index, to_index = match_from_index + size, match_to_index + size
211
+ if size > 0
212
+ operations << [:equal,
213
+ match_from_index, from_index,
214
+ match_to_index, to_index]
215
+ end
216
+ end
217
+ operations
218
+ end
219
+
220
+ def compute_ratio
221
+ matches = blocks.inject(0) {|result, block| result + block[-1]}
222
+ length = @from.length + @to.length
223
+ if length.zero?
224
+ 1.0
225
+ else
226
+ 2.0 * matches / length
227
+ end
228
+ end
229
+
230
+ def determine_tag(from_index, to_index,
231
+ match_from_index, match_to_index)
232
+ if from_index < match_from_index and to_index < match_to_index
233
+ :replace
234
+ elsif from_index < match_from_index
235
+ :delete
236
+ elsif to_index < match_to_index
237
+ :insert
238
+ else
239
+ :equal
240
+ end
241
+ end
242
+
243
+ def expand_edge_equal_operations!(_operations, context_size)
244
+ tag, from_start, from_end, to_start, to_end = _operations[0]
245
+ if tag == :equal
246
+ _operations[0] = [tag,
247
+ [from_start, from_end - context_size].max,
248
+ from_end,
249
+ [to_start, to_end - context_size].max,
250
+ to_end]
251
+ end
252
+
253
+ tag, from_start, from_end, to_start, to_end = _operations[-1]
254
+ if tag == :equal
255
+ _operations[-1] = [tag,
256
+ from_start,
257
+ [from_end, from_start + context_size].min,
258
+ to_start,
259
+ [to_end, to_start + context_size].min]
260
+ end
261
+ end
262
+ end
263
+
264
+ class Differ
265
+ def initialize(from, to)
266
+ @from = from
267
+ @to = to
268
+ end
269
+
270
+ private
271
+ def tag(mark, contents)
272
+ contents.collect {|content| "#{mark}#{content}"}
273
+ end
274
+ end
275
+
276
+ class UTF8Line
277
+ class << self
278
+ # from http://unicode.org/reports/tr11/
279
+ WIDE_CHARACTERS =
280
+ [0x1100..0x1159, 0x115F..0x115F, 0x2329..0x232A,
281
+ 0x2E80..0x2E99, 0x2E9B..0x2EF3, 0x2F00..0x2FD5,
282
+ 0x2FF0..0x2FFB, 0x3000..0x303E, 0x3041..0x3096,
283
+ 0x3099..0x30FF, 0x3105..0x312D, 0x3131..0x318E,
284
+ 0x3190..0x31B7, 0x31C0..0x31E3, 0x31F0..0x321E,
285
+ 0x3220..0x3243, 0x3250..0x32FE, 0x3300..0x4DB5,
286
+ 0x4E00..0x9FC3, 0xA000..0xA48C, 0xA490..0xA4C6,
287
+ 0xAC00..0xD7A3, 0xF900..0xFA2D, 0xFA30..0xFA6A,
288
+ 0xFA70..0xFAD9, 0xFE10..0xFE19, 0xFE30..0xFE52,
289
+ 0xFE54..0xFE66, 0xFE68..0xFE6B, 0xFF01..0xFF60,
290
+ 0xFFE0..0xFFE6, 0x20000..0x2FFFD, 0x30000..0x3FFFD,
291
+ ]
292
+
293
+ AMBIGUOUS =
294
+ [0x00A1..0x00A1, 0x00A4..0x00A4, 0x00A7..0x00A8,
295
+ 0x00AA..0x00AA, 0x00AD..0x00AE, 0x00B0..0x00B4,
296
+ 0x00B6..0x00BA, 0x00BC..0x00BF, 0x00C6..0x00C6,
297
+ 0x00D0..0x00D0, 0x00D7..0x00D8, 0x00DE..0x00E1,
298
+ 0x00E6..0x00E6, 0x00E8..0x00EA, 0x00EC..0x00ED,
299
+ 0x00F0..0x00F0, 0x00F2..0x00F3, 0x00F7..0x00FA,
300
+ 0x00FC..0x00FC, 0x00FE..0x00FE, 0x0101..0x0101,
301
+ 0x0111..0x0111, 0x0113..0x0113, 0x011B..0x011B,
302
+ 0x0126..0x0127, 0x012B..0x012B, 0x0131..0x0133,
303
+ 0x0138..0x0138, 0x013F..0x0142, 0x0144..0x0144,
304
+ 0x0148..0x014B, 0x014D..0x014D, 0x0152..0x0153,
305
+ 0x0166..0x0167, 0x016B..0x016B, 0x01CE..0x01CE,
306
+ 0x01D0..0x01D0, 0x01D2..0x01D2, 0x01D4..0x01D4,
307
+ 0x01D6..0x01D6, 0x01D8..0x01D8, 0x01DA..0x01DA,
308
+ 0x01DC..0x01DC, 0x0251..0x0251, 0x0261..0x0261,
309
+ 0x02C4..0x02C4, 0x02C7..0x02C7, 0x02C9..0x02CB,
310
+ 0x02CD..0x02CD, 0x02D0..0x02D0, 0x02D8..0x02DB,
311
+ 0x02DD..0x02DD, 0x02DF..0x02DF, 0x0300..0x036F,
312
+ 0x0391..0x03A1, 0x03A3..0x03A9, 0x03B1..0x03C1,
313
+ 0x03C3..0x03C9, 0x0401..0x0401, 0x0410..0x044F,
314
+ 0x0451..0x0451, 0x2010..0x2010, 0x2013..0x2016,
315
+ 0x2018..0x2019, 0x201C..0x201D, 0x2020..0x2022,
316
+ 0x2024..0x2027, 0x2030..0x2030, 0x2032..0x2033,
317
+ 0x2035..0x2035, 0x203B..0x203B, 0x203E..0x203E,
318
+ 0x2074..0x2074, 0x207F..0x207F, 0x2081..0x2084,
319
+ 0x20AC..0x20AC, 0x2103..0x2103, 0x2105..0x2105,
320
+ 0x2109..0x2109, 0x2113..0x2113, 0x2116..0x2116,
321
+ 0x2121..0x2122, 0x2126..0x2126, 0x212B..0x212B,
322
+ 0x2153..0x2154, 0x215B..0x215E, 0x2160..0x216B,
323
+ 0x2170..0x2179, 0x2190..0x2199, 0x21B8..0x21B9,
324
+ 0x21D2..0x21D2, 0x21D4..0x21D4, 0x21E7..0x21E7,
325
+ 0x2200..0x2200, 0x2202..0x2203, 0x2207..0x2208,
326
+ 0x220B..0x220B, 0x220F..0x220F, 0x2211..0x2211,
327
+ 0x2215..0x2215, 0x221A..0x221A, 0x221D..0x2220,
328
+ 0x2223..0x2223, 0x2225..0x2225, 0x2227..0x222C,
329
+ 0x222E..0x222E, 0x2234..0x2237, 0x223C..0x223D,
330
+ 0x2248..0x2248, 0x224C..0x224C, 0x2252..0x2252,
331
+ 0x2260..0x2261, 0x2264..0x2267, 0x226A..0x226B,
332
+ 0x226E..0x226F, 0x2282..0x2283, 0x2286..0x2287,
333
+ 0x2295..0x2295, 0x2299..0x2299, 0x22A5..0x22A5,
334
+ 0x22BF..0x22BF, 0x2312..0x2312, 0x2460..0x24E9,
335
+ 0x24EB..0x254B, 0x2550..0x2573, 0x2580..0x258F,
336
+ 0x2592..0x2595, 0x25A0..0x25A1, 0x25A3..0x25A9,
337
+ 0x25B2..0x25B3, 0x25B6..0x25B7, 0x25BC..0x25BD,
338
+ 0x25C0..0x25C1, 0x25C6..0x25C8, 0x25CB..0x25CB,
339
+ 0x25CE..0x25D1, 0x25E2..0x25E5, 0x25EF..0x25EF,
340
+ 0x2605..0x2606, 0x2609..0x2609, 0x260E..0x260F,
341
+ 0x2614..0x2615, 0x261C..0x261C, 0x261E..0x261E,
342
+ 0x2640..0x2640, 0x2642..0x2642, 0x2660..0x2661,
343
+ 0x2663..0x2665, 0x2667..0x266A, 0x266C..0x266D,
344
+ 0x266F..0x266F, 0x273D..0x273D, 0x2776..0x277F,
345
+ 0xE000..0xF8FF, 0xFE00..0xFE0F, 0xFFFD..0xFFFD,
346
+ 0xE0100..0xE01EF, 0xF0000..0xFFFFD, 0x100000..0x10FFFD,
347
+ ]
348
+
349
+ def wide_character?(character)
350
+ binary_search_ranges(character, WIDE_CHARACTERS) or
351
+ binary_search_ranges(character, AMBIGUOUS)
352
+ end
353
+
354
+ private
355
+ def binary_search_ranges(character, ranges)
356
+ if ranges.size.zero?
357
+ false
358
+ elsif ranges.size == 1
359
+ ranges[0].include?(character)
360
+ else
361
+ half = ranges.size / 2
362
+ range = ranges[half]
363
+ if range.include?(character)
364
+ true
365
+ elsif character < range.begin
366
+ binary_search_ranges(character, ranges[0...half])
367
+ else
368
+ binary_search_ranges(character, ranges[(half + 1)..-1])
369
+ end
370
+ end
371
+ end
372
+ end
373
+
374
+ def initialize(line)
375
+ @line = line
376
+ @characters = @line.unpack("U*")
377
+ end
378
+
379
+ def [](*args)
380
+ result = @characters[*args]
381
+ if result.respond_to?(:pack)
382
+ result.pack("U*")
383
+ else
384
+ result
385
+ end
386
+ end
387
+
388
+ def each(&block)
389
+ @characters.each(&block)
390
+ end
391
+
392
+ def size
393
+ @characters.size
394
+ end
395
+
396
+ def to_s
397
+ @line
398
+ end
399
+
400
+ def compute_width(start, _end)
401
+ width = 0
402
+ start.upto(_end - 1) do |i|
403
+ if self.class.wide_character?(@characters[i])
404
+ width += 2
405
+ else
406
+ width += 1
407
+ end
408
+ end
409
+ width
410
+ end
411
+ end
412
+
413
+ class ReadableDiffer < Differ
414
+ def diff(options={})
415
+ @result = []
416
+ operations.each do |tag, from_start, from_end, to_start, to_end|
417
+ case tag
418
+ when :replace
419
+ diff_lines(from_start, from_end, to_start, to_end)
420
+ when :delete
421
+ tag_deleted(@from[from_start...from_end])
422
+ when :insert
423
+ tag_inserted(@to[to_start...to_end])
424
+ when :equal
425
+ tag_equal(@from[from_start...from_end])
426
+ else
427
+ raise "unknown tag: #{tag}"
428
+ end
429
+ end
430
+ @result
431
+ end
432
+
433
+ private
434
+ def operations
435
+ @operations ||= nil
436
+ if @operations.nil?
437
+ matcher = SequenceMatcher.new(@from, @to)
438
+ @operations = matcher.operations
439
+ end
440
+ @operations
441
+ end
442
+
443
+ def default_ratio
444
+ 0.74
445
+ end
446
+
447
+ def cut_off_ratio
448
+ 0.75
449
+ end
450
+
451
+ def tag(mark, contents)
452
+ contents.each do |content|
453
+ @result << "#{mark}#{content}"
454
+ end
455
+ end
456
+
457
+ def tag_deleted(contents)
458
+ tag("- ", contents)
459
+ end
460
+
461
+ def tag_inserted(contents)
462
+ tag("+ ", contents)
463
+ end
464
+
465
+ def tag_equal(contents)
466
+ tag(" ", contents)
467
+ end
468
+
469
+ def tag_difference(contents)
470
+ tag("? ", contents)
471
+ end
472
+
473
+ def find_diff_line_info(from_start, from_end, to_start, to_end)
474
+ best_ratio = default_ratio
475
+ from_equal_index = to_equal_index = nil
476
+ from_best_index = to_best_index = nil
477
+
478
+ to_start.upto(to_end - 1) do |to_index|
479
+ from_start.upto(from_end - 1) do |from_index|
480
+ if @from[from_index] == @to[to_index]
481
+ from_equal_index ||= from_index
482
+ to_equal_index ||= to_index
483
+ next
484
+ end
485
+
486
+ matcher = SequenceMatcher.new(@from[from_index], @to[to_index],
487
+ &method(:space_character?))
488
+ if matcher.ratio > best_ratio
489
+ best_ratio = matcher.ratio
490
+ from_best_index = from_index
491
+ to_best_index = to_index
492
+ end
493
+ end
494
+ end
495
+
496
+ [best_ratio,
497
+ from_equal_index, to_equal_index,
498
+ from_best_index, to_best_index]
499
+ end
500
+
501
+ def diff_lines(from_start, from_end, to_start, to_end)
502
+ info = find_diff_line_info(from_start, from_end, to_start, to_end)
503
+ best_ratio, from_equal_index, to_equal_index, *info = info
504
+ from_best_index, to_best_index = info
505
+ from_best_index ||= from_start
506
+ to_best_index ||= to_start
507
+
508
+ if best_ratio < cut_off_ratio
509
+ if from_equal_index.nil?
510
+ if to_end - to_start < from_end - from_start
511
+ tag_inserted(@to[to_start...to_end])
512
+ tag_deleted(@from[from_start...from_end])
513
+ else
514
+ tag_deleted(@from[from_start...from_end])
515
+ tag_inserted(@to[to_start...to_end])
516
+ end
517
+ return
518
+ end
519
+ from_best_index = from_equal_index
520
+ to_best_index = to_equal_index
521
+ best_ratio = 1.0
522
+ end
523
+
524
+ _diff_lines(from_start, from_best_index, to_start, to_best_index)
525
+ diff_line(@from[from_best_index], @to[to_best_index])
526
+ _diff_lines(from_best_index + 1, from_end, to_best_index + 1, to_end)
527
+ end
528
+
529
+ def _diff_lines(from_start, from_end, to_start, to_end)
530
+ if from_start < from_end
531
+ if to_start < to_end
532
+ diff_lines(from_start, from_end, to_start, to_end)
533
+ else
534
+ tag_deleted(@from[from_start...from_end])
535
+ end
536
+ else
537
+ tag_inserted(@to[to_start...to_end])
538
+ end
539
+ end
540
+
541
+ def line_operations(from_line, to_line)
542
+ if !from_line.respond_to?(:force_encoding) and $KCODE == "UTF8"
543
+ from_line = UTF8Line.new(from_line)
544
+ to_line = UTF8Line.new(to_line)
545
+ end
546
+ matcher = SequenceMatcher.new(from_line, to_line,
547
+ &method(:space_character?))
548
+ [from_line, to_line, matcher.operations]
549
+ end
550
+
551
+ def compute_width(line, start, _end)
552
+ if line.respond_to?(:encoding) and
553
+ Encoding.compatible?(Encoding::UTF_8, line.encoding)
554
+ utf8_line = line[start..._end].encode(Encoding::UTF_8)
555
+ width = 0
556
+ utf8_line.each_codepoint do |unicode_codepoint|
557
+ if UTF8Line.wide_character?(unicode_codepoint)
558
+ width += 2
559
+ else
560
+ width += 1
561
+ end
562
+ end
563
+ width
564
+ elsif line.is_a?(UTF8Line)
565
+ line.compute_width(start, _end)
566
+ else
567
+ _end - start
568
+ end
569
+ end
570
+
571
+ def diff_line(from_line, to_line)
572
+ from_tags = ""
573
+ to_tags = ""
574
+ from_line, to_line, _operations = line_operations(from_line, to_line)
575
+ _operations.each do |tag, from_start, from_end, to_start, to_end|
576
+ from_width = compute_width(from_line, from_start, from_end)
577
+ to_width = compute_width(to_line, to_start, to_end)
578
+ case tag
579
+ when :replace
580
+ from_tags << "^" * from_width
581
+ to_tags << "^" * to_width
582
+ when :delete
583
+ from_tags << "-" * from_width
584
+ when :insert
585
+ to_tags << "+" * to_width
586
+ when :equal
587
+ from_tags << " " * from_width
588
+ to_tags << " " * to_width
589
+ else
590
+ raise "unknown tag: #{tag}"
591
+ end
592
+ end
593
+ format_diff_point(from_line, to_line, from_tags, to_tags)
594
+ end
595
+
596
+ def format_diff_point(from_line, to_line, from_tags, to_tags)
597
+ common = [n_leading_characters(from_line, ?\t),
598
+ n_leading_characters(to_line, ?\t)].min
599
+ common = [common,
600
+ n_leading_characters(from_tags[0, common], " "[0])].min
601
+ from_tags = from_tags[common..-1].rstrip
602
+ to_tags = to_tags[common..-1].rstrip
603
+
604
+ result = tag_deleted([from_line])
605
+ unless from_tags.empty?
606
+ tag_difference(["#{"\t" * common}#{from_tags}"])
607
+ end
608
+ tag_inserted([to_line])
609
+ unless to_tags.empty?
610
+ tag_difference(["#{"\t" * common}#{to_tags}"])
611
+ end
612
+ end
613
+
614
+ def n_leading_characters(string, character)
615
+ n = 0
616
+ while string[n] == character
617
+ n += 1
618
+ end
619
+ n
620
+ end
621
+
622
+ def space_character?(character)
623
+ [" "[0], "\t"[0]].include?(character)
624
+ end
625
+ end
626
+
627
+ class UnifiedDiffer < Differ
628
+ def diff(options={})
629
+ groups = SequenceMatcher.new(@from, @to).grouped_operations
630
+ return [] if groups.empty?
631
+ return [] if same_content?(groups)
632
+
633
+ show_context = options[:show_context]
634
+ show_context = true if show_context.nil?
635
+ result = ["--- #{options[:from_label]}".rstrip,
636
+ "+++ #{options[:to_label]}".rstrip]
637
+ groups.each do |operations|
638
+ result << format_summary(operations, show_context)
639
+ operations.each do |args|
640
+ operation_tag, from_start, from_end, to_start, to_end = args
641
+ case operation_tag
642
+ when :replace
643
+ result.concat(tag("-", @from[from_start...from_end]))
644
+ result.concat(tag("+", @to[to_start...to_end]))
645
+ when :delete
646
+ result.concat(tag("-", @from[from_start...from_end]))
647
+ when :insert
648
+ result.concat(tag("+", @to[to_start...to_end]))
649
+ when :equal
650
+ result.concat(tag(" ", @from[from_start...from_end]))
651
+ end
652
+ end
653
+ end
654
+ result
655
+ end
656
+
657
+ private
658
+ def same_content?(groups)
659
+ return false if groups.size != 1
660
+ group = groups[0]
661
+ return false if group.size != 1
662
+ tag, from_start, from_end, to_start, to_end = group[0]
663
+
664
+ tag == :equal and [from_start, from_end] == [to_start, to_end]
665
+ end
666
+
667
+ def format_summary(operations, show_context)
668
+ _, first_from_start, _, first_to_start, _ = operations[0]
669
+ _, _, last_from_end, _, last_to_end = operations[-1]
670
+ summary = "@@ -%d,%d +%d,%d @@" % [first_from_start + 1,
671
+ last_from_end - first_from_start,
672
+ first_to_start + 1,
673
+ last_to_end - first_to_start,]
674
+ if show_context
675
+ interesting_line = find_interesting_line(first_from_start,
676
+ first_to_start,
677
+ :define_line?)
678
+ summary << " #{interesting_line}" if interesting_line
679
+ end
680
+ summary
681
+ end
682
+
683
+ def find_interesting_line(from_start, to_start, predicate)
684
+ from_index = from_start
685
+ to_index = to_start
686
+ while from_index >= 0 or to_index >= 0
687
+ [@from[from_index], @to[to_index]].each do |line|
688
+ return line if line and send(predicate, line)
689
+ end
690
+
691
+ from_index -= 1
692
+ to_index -= 1
693
+ end
694
+ nil
695
+ end
696
+
697
+ def define_line?(line)
698
+ /\A(?:[_a-zA-Z$]|\s*(?:class|module|def)\b)/ =~ line
699
+ end
700
+ end
701
+
702
+ module_function
703
+ def need_fold?(diff)
704
+ /^[-+].{79}/ =~ diff
705
+ end
706
+
707
+ def fold(string)
708
+ string.split(/\r?\n/).collect do |line|
709
+ line.gsub(/(.{78})/, "\\1\n")
710
+ end.join("\n")
711
+ end
712
+
713
+ def folded_readable(from, to, options={})
714
+ readable(fold(from), fold(to), options)
715
+ end
716
+
717
+ def readable(from, to, options={})
718
+ diff(ReadableDiffer, from, to, options)
719
+ end
720
+
721
+ def unified(from, to, options={})
722
+ diff(UnifiedDiffer, from, to, options)
723
+ end
724
+
725
+ def diff(differ_class, from, to, options={})
726
+ differ = differ_class.new(from.split(/\r?\n/), to.split(/\r?\n/))
727
+ lines = differ.diff(options)
728
+ if Object.const_defined?(:EncodingError)
729
+ begin
730
+ lines.join("\n")
731
+ rescue EncodingError
732
+ lines.collect {|line| line.force_encoding("ASCII-8BIT")}.join("\n")
733
+ end
734
+ else
735
+ lines.join("\n")
736
+ end
737
+ end
738
+ end
739
+ end
740
+ end