rroonga 0.9.4-x86-mingw32 → 0.9.5-x86-mingw32

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