docdiff 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/.gitignore +6 -0
  2. data/.travis.yml +7 -0
  3. data/Gemfile +17 -0
  4. data/Guardfile +8 -0
  5. data/Makefile +108 -0
  6. data/Rakefile +17 -0
  7. data/bin/docdiff +179 -0
  8. data/devutil/JIS0208.TXT +6952 -0
  9. data/devutil/char_by_charclass.rb +23 -0
  10. data/devutil/charclass_by_char.rb +21 -0
  11. data/devutil/jis0208.rb +343 -0
  12. data/devutil/testjis0208.rb +38 -0
  13. data/docdiff.conf.example +22 -0
  14. data/docdiff.gemspec +23 -0
  15. data/docdiffwebui.cgi +176 -0
  16. data/docdiffwebui.html +123 -0
  17. data/img/docdiff-screenshot-format-html-digest-firefox.png +0 -0
  18. data/img/docdiff-screenshot-format-html-firefox.png +0 -0
  19. data/img/docdiff-screenshot-format-tty-cmdexe-en.png +0 -0
  20. data/img/docdiff-screenshot-format-tty-cmdexe-ja.png +0 -0
  21. data/img/docdiff-screenshot-format-tty-rxvtunicode-en.png +0 -0
  22. data/img/docdiff-screenshot-format-tty-rxvtunicode-ja.png +0 -0
  23. data/img/docdiff-screenshot-format-tty-xterm-en.png +0 -0
  24. data/img/docdiff-screenshot-format-tty-xterm-ja.png +0 -0
  25. data/img/docdiff-screenshot-resolution-linewordchar-xterm.png +0 -0
  26. data/index.html +181 -0
  27. data/langfilter.rb +14 -0
  28. data/lib/doc_diff.rb +170 -0
  29. data/lib/docdiff.rb +7 -0
  30. data/lib/docdiff/charstring.rb +579 -0
  31. data/lib/docdiff/diff.rb +217 -0
  32. data/lib/docdiff/diff/contours.rb +382 -0
  33. data/lib/docdiff/diff/editscript.rb +148 -0
  34. data/lib/docdiff/diff/rcsdiff.rb +107 -0
  35. data/lib/docdiff/diff/shortestpath.rb +93 -0
  36. data/lib/docdiff/diff/speculative.rb +40 -0
  37. data/lib/docdiff/diff/subsequence.rb +39 -0
  38. data/lib/docdiff/diff/unidiff.rb +124 -0
  39. data/lib/docdiff/difference.rb +92 -0
  40. data/lib/docdiff/document.rb +127 -0
  41. data/lib/docdiff/encoding/en_ascii.rb +97 -0
  42. data/lib/docdiff/encoding/ja_eucjp.rb +269 -0
  43. data/lib/docdiff/encoding/ja_sjis.rb +260 -0
  44. data/lib/docdiff/encoding/ja_utf8.rb +6974 -0
  45. data/lib/docdiff/version.rb +3 -0
  46. data/lib/docdiff/view.rb +476 -0
  47. data/lib/viewdiff.rb +375 -0
  48. data/readme.html +713 -0
  49. data/sample/01.en.ascii.cr +1 -0
  50. data/sample/01.en.ascii.crlf +2 -0
  51. data/sample/01.en.ascii.lf +2 -0
  52. data/sample/01.ja.eucjp.lf +2 -0
  53. data/sample/01.ja.sjis.cr +1 -0
  54. data/sample/01.ja.sjis.crlf +2 -0
  55. data/sample/01.ja.utf8.crlf +2 -0
  56. data/sample/02.en.ascii.cr +1 -0
  57. data/sample/02.en.ascii.crlf +2 -0
  58. data/sample/02.en.ascii.lf +2 -0
  59. data/sample/02.ja.eucjp.lf +2 -0
  60. data/sample/02.ja.sjis.cr +1 -0
  61. data/sample/02.ja.sjis.crlf +2 -0
  62. data/sample/02.ja.utf8.crlf +2 -0
  63. data/sample/humpty_dumpty01.ascii.lf +4 -0
  64. data/sample/humpty_dumpty02.ascii.lf +4 -0
  65. data/test/charstring_test.rb +1008 -0
  66. data/test/diff_test.rb +36 -0
  67. data/test/difference_test.rb +64 -0
  68. data/test/docdiff_test.rb +193 -0
  69. data/test/document_test.rb +626 -0
  70. data/test/test_helper.rb +7 -0
  71. data/test/view_test.rb +570 -0
  72. data/test/viewdiff_test.rb +908 -0
  73. metadata +129 -0
@@ -0,0 +1,3 @@
1
+ module Docdiff
2
+ VERSION = "0.5.0"
3
+ end
@@ -0,0 +1,476 @@
1
+ #!/usr/bin/ruby
2
+
3
+ class String
4
+ def scan_lines(eol)
5
+ case eol
6
+ when nil then scan(/\A.*\Z/m)
7
+ when "CR" then scan(/.*?\r|[^\r]+\Z/m)
8
+ when "LF" then scan(/.*?\n|[^\n]+\Z/m)
9
+ when "CRLF" then scan(/.*?\r\n|.+\Z/m)
10
+ else raise "#{eol} is not supported.\n"
11
+ end
12
+ end
13
+ def scan_eols(eol)
14
+ case eol
15
+ when nil then []
16
+ when "CR" then scan(/\r/m)
17
+ when "LF" then scan(/\n/m)
18
+ when "CRLF" then scan(/\r\n/m)
19
+ else raise "#{eol} is not supported.\n"
20
+ end
21
+ end
22
+ end
23
+
24
+ class View
25
+
26
+ # EOL_CHARS_PAT = Regexp.new(/\r\n|\r(?!\n)|(?:\A|[^\r])\n/m)
27
+
28
+ def initialize(difference, encoding, eol)
29
+ @difference = difference
30
+ @encoding = encoding
31
+ @eol = eol
32
+ @eol_char = {'CR'=>"\r", 'LF'=>"\n", 'CRLF'=>"\r\n"}[@eol]
33
+ # if CharString::EOLChars[@eol]
34
+ # @eol_char = CharString::EOLChars[@eol].eol_char
35
+ # else
36
+ # @eol_char = nil
37
+ # end
38
+ end
39
+
40
+ def difference_whole()
41
+ @difference
42
+ end
43
+
44
+ def difference_digest()
45
+ #
46
+ end
47
+
48
+ def escape_inside(str, tags)
49
+ str.gsub(tags[:inside_escape_pat]){|m| tags[:inside_escape_dic][m]}
50
+ end
51
+ def escape_outside(str, tags)
52
+ str.gsub(tags[:outside_escape_pat]){|m| tags[:outside_escape_dic][m]}
53
+ end
54
+
55
+ def apply_style(tags, headfoot = true)
56
+ result = []
57
+ @difference.each{|block|
58
+ operation = block.first
59
+ if block_given?
60
+ src = yield((block[1] || []).join)
61
+ tgt = yield((block[1] || []).join)
62
+ else
63
+ src = (block[1] || []).join
64
+ tgt = (block[2] || []).join
65
+ end
66
+ case operation
67
+ when :common_elt_elt
68
+ result << (tags[:start_common] + escape_outside(src, tags) + tags[:end_common])
69
+ when :change_elt
70
+ result << (tags[:start_before_change] + escape_inside(src, tags) + tags[:end_before_change] +
71
+ tags[:start_after_change] + escape_inside(tgt, tags) + tags[:end_after_change])
72
+ when :del_elt
73
+ result << (tags[:start_del] + escape_inside(src, tags) + tags[:end_del])
74
+ when :add_elt
75
+ result << (tags[:start_add] + escape_inside(tgt, tags) + tags[:end_add])
76
+ else
77
+ raise "invalid attribute: #{block.first}\n"
78
+ end
79
+ }
80
+ if headfoot == true
81
+ result = tags[:header] + result + tags[:footer]
82
+ end
83
+ result.delete_if{|elem|elem==''}
84
+ end
85
+
86
+ def encname_for_regexp(encname)
87
+ def ruby_m17n?
88
+ "".respond_to?(:force_encoding)
89
+ end
90
+ if ruby_m17n?
91
+ # in 1.9.x, encoding names are deprecated except for N (ASCII-8BIT (binary))
92
+ nil
93
+ else
94
+ # in 1.8.x, U|E|S|N are accepted
95
+ encname.sub(/US-ASCII/i, 'none')
96
+ end
97
+ end
98
+
99
+ CONTEXT_PRE_LENGTH = 32
100
+ CONTEXT_POST_LENGTH = 32
101
+ def apply_style_digest(tags, headfoot = true)
102
+ cxt_pre_pat = Regexp.new('.{0,'+"#{CONTEXT_PRE_LENGTH}"+'}\Z',
103
+ Regexp::MULTILINE, encname_for_regexp(@encoding))
104
+ cxt_post_pat = Regexp.new('\A.{0,'+"#{CONTEXT_POST_LENGTH}"+'}',
105
+ Regexp::MULTILINE, encname_for_regexp(@encoding))
106
+ display = (tags and tags[:display]) || 'inline'
107
+ result = []
108
+ d1l = doc1_line_number = 1
109
+ d2l = doc2_line_number = 1
110
+ @difference.each_with_index{|entry, i|
111
+ if block_given?
112
+ src = yield((entry[1] || []).join)
113
+ tgt = yield((entry[1] || []).join)
114
+ else
115
+ src = (entry[1] || []).join
116
+ tgt = (entry[2] || []).join
117
+ end
118
+ cxt_pre = if i == 0
119
+ "" # no pre context for the first entry
120
+ else
121
+ (@difference[i-1][1] || []).join.scan(cxt_pre_pat).join
122
+ end
123
+ cxt_post = if (i + 1) == @difference.size
124
+ "" # no post context for the last entry
125
+ else
126
+ (@difference[i+1][1] || []).join.scan(cxt_post_pat).join
127
+ end
128
+ # elements for an entry
129
+ e_head = Proc.new {|pos_str|
130
+ tags[:start_entry] + tags[:start_position] + pos_str + tags[:end_position]}
131
+ e_cxt_pre = tags[:start_prefix] + escape_outside(cxt_pre, tags) + tags[:end_prefix]
132
+ e_src = escape_inside(src, tags)
133
+ e_chg = tags[:start_before_change] + escape_inside(src, tags) + tags[:end_before_change] +
134
+ tags[:start_after_change] + escape_inside(tgt, tags) + tags[:end_after_change]
135
+ e_chgdel = tags[:start_before_change] + escape_inside(src, tags) + tags[:end_before_change]
136
+ e_chgadd = tags[:start_after_change] + escape_inside(tgt, tags) + tags[:end_after_change]
137
+ e_del = tags[:start_del] + escape_outside(src, tags) + tags[:end_del]
138
+ e_add = tags[:start_add] + escape_outside(tgt, tags) + tags[:end_add]
139
+ e_cxt_post = tags[:start_postfix] + escape_outside(cxt_post, tags) + tags[:end_postfix]
140
+ e_foot = tags[:end_entry] + (@eol_char||"")
141
+
142
+ span1 = source_lines_involved = src.scan_lines(@eol).size
143
+ span2 = target_lines_involved = tgt.scan_lines(@eol).size
144
+ pos_str = ""
145
+ case operation = entry.first
146
+ when :common_elt_elt
147
+ # skipping common part
148
+ when :change_elt
149
+ pos_str = "#{d1l}" + "#{if span1 > 1 then '-'+(d1l + span1 - 1).to_s; end}" +
150
+ ",#{d2l}" + "#{if span2 > 1 then '-'+(d2l + span2 - 1).to_s; end}"
151
+ case display
152
+ when 'inline'
153
+ result << (e_head.call(pos_str) + e_cxt_pre + e_chg + e_cxt_post + e_foot)
154
+ when 'multi'
155
+ result << (e_head.call(pos_str) + e_cxt_pre + e_chgdel + e_cxt_post +
156
+ e_cxt_pre + e_chgadd + e_cxt_post + e_foot)
157
+ else raise "Unsupported display type: #{display}"
158
+ end
159
+ when :del_elt
160
+ pos_str = "#{d1l}" + "#{if span1 > 1 then '-'+(d1l + span1 - 1).to_s; end}" +
161
+ ",(#{d2l})"
162
+ case display
163
+ when 'inline'
164
+ result << (e_head.call(pos_str) + e_cxt_pre + e_del + e_cxt_post + e_foot)
165
+ when 'multi'
166
+ result << (e_head.call(pos_str) + e_cxt_pre + e_src + e_cxt_post +
167
+ e_cxt_pre + e_del + e_cxt_post + e_foot)
168
+ else raise "Unsupported display type: #{display}"
169
+ end
170
+ when :add_elt
171
+ pos_str = "(#{d1l})" +
172
+ ",#{d2l}" + "#{if span2 > 1 then '-'+(d2l + span2 - 1).to_s; end}"
173
+ case display
174
+ when 'inline'
175
+ result << (e_head.call(pos_str) + e_cxt_pre + e_add + e_cxt_post + e_foot)
176
+ when 'multi'
177
+ result << (e_head.call(pos_str) + e_cxt_pre + e_src + e_cxt_post +
178
+ e_cxt_pre + e_add + e_cxt_post + e_foot)
179
+ else raise "Unsupported display type: #{display}"
180
+ end
181
+ else
182
+ raise "invalid attribute: #{block.first}\n"
183
+ end
184
+ d1l += src.scan_eols(@eol).size
185
+ d2l += tgt.scan_eols(@eol).size
186
+ }
187
+ result.unshift(tags[:start_digest_body])
188
+ result.push(tags[:end_digest_body])
189
+ result = tags[:header] + result + tags[:footer] if headfoot == true
190
+ result.delete_if{|elem| elem == ''}
191
+ end
192
+
193
+ def source_lines()
194
+ if @source_lines == nil
195
+ @source_lines = @difference.collect{|entry| entry[1]}.join.scan_lines(@eol)
196
+ end
197
+ @source_lines
198
+ end
199
+ def target_lines()
200
+ if @target_lines == nil
201
+ @target_lines = @difference.collect{|entry| entry[2]}.join.scan_lines(@eol)
202
+ end
203
+ @target_lines
204
+ end
205
+
206
+ # tty (terminal)
207
+ def tty_header()
208
+ []
209
+ end
210
+ def tty_footer()
211
+ []
212
+ end
213
+ TTYEscapeDic = {'ThisRandomString' => 'ThisRandomString'}
214
+ TTYEscapePat = /(\r\n|#{TTYEscapeDic.keys.collect{|k|Regexp.quote(k)}.join('|')})/m
215
+ def tty_tags()
216
+ {:outside_escape_dic => TTYEscapeDic,
217
+ :outside_escape_pat => TTYEscapePat,
218
+ :inside_escape_dic => TTYEscapeDic,
219
+ :inside_escape_pat => TTYEscapePat,
220
+ :start_digest_body => "----#{@eol_char||''}",
221
+ :end_digest_body => '',
222
+ :start_entry => '',
223
+ :end_entry => "----",
224
+ :start_position => '',
225
+ :end_position => "#{@eol_char||''}",
226
+ :start_prefix => '',
227
+ :end_prefix => '',
228
+ :start_postfix => '',
229
+ :end_postfix => "#{@eol_char||''}",
230
+ :header => tty_header(),
231
+ :footer => tty_footer(),
232
+ :start_common => '',
233
+ :end_common => '',
234
+ :start_del => "\033[7;4;31m", # Inverted, Underlined, Red
235
+ :end_del => "\033[0m",
236
+ :start_add => "\033[7;1;34m", # Inverted, Bold, Blue
237
+ :end_add => "\033[0m",
238
+ :start_before_change => "\033[7;4;33m", # Inverted, Underlined, Yellow
239
+ :end_before_change => "\033[0m",
240
+ :start_after_change => "\033[7;1;32m", # Inverted, Bold, Green
241
+ :end_after_change => "\033[0m"}
242
+ end
243
+ def to_tty(overriding_opts = nil, headfoot = true) # color escape sequence
244
+ tags = tty_tags()
245
+ tags.update(overriding_opts) if overriding_opts
246
+ apply_style(tags, headfoot)
247
+ end
248
+ def to_tty_digest(overriding_opts = nil, headfoot = true)
249
+ tags = tty_tags
250
+ tags.update(overriding_opts) if overriding_opts
251
+ apply_style_digest(tags, headfoot)
252
+ end
253
+
254
+ # HTML (XHTML)
255
+ def html_header()
256
+ ["<?xml version=\"1.0\" encoding=\"#{@encoding||''}\"?>#{@eol_char||''}",
257
+ "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"#{@eol_char||''}",
258
+ "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">#{@eol_char||''}",
259
+ "<html><head>#{@eol_char||''}",
260
+ "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=#{@encoding||''}\" />#{@eol_char||''}",
261
+ "<title>Difference</title>#{@eol_char||''}",
262
+ "<style type=\"text/css\">#{@eol_char||''}" +
263
+ " body {font-family: monospace;}#{@eol_char||''}" +
264
+ " span.del {background: hotpink; border: thin inset;}#{@eol_char||''}" +
265
+ " span.add {background: deepskyblue; font-weight: bolder; border: thin outset;}#{@eol_char||''}" +
266
+ " span.before-change {background: yellow; border: thin inset;}#{@eol_char||''}" +
267
+ " span.after-change {background: lime; font-weight: bolder; border: thin outset;}#{@eol_char||''}" +
268
+ " li.entry .position {font-weight: bolder; margin-top: 0em; margin-bottom: 0em; padding-top: 0.5em; padding-bottom: 0em;}#{@eol_char||''}" +
269
+ " li.entry .body {margin-top: 0em; margin-bottom: 0em; padding-top: 0em; padding-bottom: 0.5em;}#{@eol_char||''}" +
270
+ " li.entry {border-top: thin solid gray;}#{@eol_char||''}" +
271
+ "</style>#{@eol_char||''}",
272
+ "</head><body><div>#{@eol_char||''}"]
273
+ end
274
+ def html_footer()
275
+ [(@eol_char||"") + '</div></body></html>' + (@eol_char||"")]
276
+ end
277
+ HTMLEscapeDic = {'<'=>'&lt;', '>'=>'&gt;', '&'=>'&amp;', ' '=>'&nbsp;&nbsp;',
278
+ "\r\n" => "<br />\r\n", "\r" => "<br />\r", "\n" => "<br />\n"}
279
+ HTMLEscapePat = /(\r\n|#{HTMLEscapeDic.keys.collect{|k|Regexp.quote(k)}.join('|')})/m
280
+ def html_tags()
281
+ {:outside_escape_dic => HTMLEscapeDic,
282
+ :outside_escape_pat => HTMLEscapePat,
283
+ :inside_escape_dic => HTMLEscapeDic,
284
+ :inside_escape_pat => HTMLEscapePat,
285
+ :start_digest_body => '<ul>',
286
+ :end_digest_body => '</ul>',
287
+ :start_entry => '<li class="entry">',
288
+ :end_entry => '</blockquote></li>',
289
+ :start_position => '<p class="position">',
290
+ :end_position => '</p><blockquote class="body">',
291
+ :start_prefix => '<p class="body">',
292
+ :end_prefix => '',
293
+ :start_postfix => '',
294
+ :end_postfix => '</p>',
295
+ :header => html_header(),
296
+ :footer => html_footer(),
297
+ :start_common => '<span class="common">',
298
+ :end_common => '</span>',
299
+ :start_del => '<span class="del"><del>',
300
+ :end_del => '</del></span>',
301
+ :start_add => '<span class="add"><ins>',
302
+ :end_add => '</ins></span>',
303
+ :start_before_change => '<span class="before-change"><del>',
304
+ :end_before_change => '</del></span>',
305
+ :start_after_change => '<span class="after-change"><ins>',
306
+ :end_after_change => '</ins></span>'}
307
+ end
308
+ def to_html(overriding_opts = nil, headfoot = true)
309
+ tags = html_tags()
310
+ tags.update(overriding_opts) if overriding_opts
311
+ apply_style(tags, headfoot)
312
+ end
313
+ def to_html_digest(overriding_opts = nil, headfoot = true)
314
+ tags = html_tags()
315
+ tags.update(overriding_opts) if overriding_opts
316
+ apply_style_digest(tags, headfoot)
317
+ end
318
+
319
+ # Manued
320
+ def manued_header()
321
+ ["defparentheses [ ]" + (@eol_char||"\n"),
322
+ "defdelete /" + (@eol_char||"\n"),
323
+ "defswap |" + (@eol_char||"\n"),
324
+ "defcomment ;" + (@eol_char||"\n"),
325
+ "defescape ~" + (@eol_char||"\n"),
326
+ "deforder newer-last" + (@eol_char||"\n"),
327
+ "defversion 0.9.5" + (@eol_char||"\n")]
328
+ end
329
+ def manued_footer()
330
+ []
331
+ end
332
+ ManuedInsideEscapeDic = {'~'=>'~~', '/'=>'~/', '['=>'~[', ']'=>'~]', ';'=>'~;'}
333
+ ManuedInsideEscapePat = /(#{ManuedInsideEscapeDic.keys.collect{|k|Regexp.quote(k)}.join('|')})/m
334
+ ManuedOutsideEscapeDic = {'~'=>'~~', '['=>'~['}
335
+ ManuedOutsideEscapePat = /(#{ManuedOutsideEscapeDic.keys.collect{|k|Regexp.quote(k)}.join('|')})/m
336
+ def manued_tags()
337
+ {:inside_escape_dic => ManuedInsideEscapeDic,
338
+ :inside_escape_pat => ManuedInsideEscapePat,
339
+ :outside_escape_dic => ManuedOutsideEscapeDic,
340
+ :outside_escape_pat => ManuedOutsideEscapePat,
341
+ :start_digest_body => "----#{@eol_char||''}",
342
+ :end_digest_body => '',
343
+ :start_entry => '',
344
+ :end_entry => "----",
345
+ :start_position => '',
346
+ :end_position => "#{@eol_char||''}",
347
+ :start_prefix => '',
348
+ :end_prefix => '',
349
+ :start_postfix => '',
350
+ :end_postfix => "#{@eol_char||''}",
351
+ :header => manued_header(),
352
+ :footer => manued_footer(),
353
+ :start_common => '',
354
+ :end_common => '',
355
+ :start_del => '[',
356
+ :end_del => '/]',
357
+ :start_add => '[/',
358
+ :end_add => ']',
359
+ :start_before_change => '[',
360
+ :end_before_change => '/',
361
+ :start_after_change => '',
362
+ :end_after_change => ']'
363
+ }
364
+ end
365
+ def to_manued(overriding_opts = nil, headfoot = true) # [ / ; ]
366
+ tags = manued_tags()
367
+ tags.update(overriding_opts) if overriding_opts
368
+ apply_style(tags, headfoot)
369
+ end
370
+ def to_manued_digest(overriding_opts = nil, headfoot = true) # [ / ; ]
371
+ tags = manued_tags()
372
+ # manued specific kludge: change should be [a/b] in inline, [a/][/b] in multi
373
+ display = (overriding_opts and overriding_opts[:display]) || 'inline'
374
+ if display == 'multi'
375
+ tags.update({:end_before_change => '/]', :start_after_change => '[/'})
376
+ end
377
+ tags.update(overriding_opts) if overriding_opts
378
+ apply_style_digest(tags, headfoot)
379
+ end
380
+
381
+ # wdiff-like
382
+ def wdiff_header()
383
+ []
384
+ end
385
+ def wdiff_footer()
386
+ []
387
+ end
388
+ WDIFFEscapeDic = {'ThisRandomString' => 'ThisRandomString'}
389
+ WDIFFEscapePat = /(\r\n|#{WDIFFEscapeDic.keys.collect{|k|Regexp.quote(k)}.join('|')})/m
390
+ def wdiff_tags()
391
+ {:outside_escape_dic => WDIFFEscapeDic,
392
+ :outside_escape_pat => WDIFFEscapePat,
393
+ :inside_escape_dic => WDIFFEscapeDic,
394
+ :inside_escape_pat => WDIFFEscapePat,
395
+ :start_digest_body => "----#{@eol_char||''}",
396
+ :end_digest_body => '',
397
+ :start_entry => '',
398
+ :end_entry => "----",
399
+ :start_position => '',
400
+ :end_position => "#{@eol_char||''}",
401
+ :start_prefix => '',
402
+ :end_prefix => '',
403
+ :start_postfix => '',
404
+ :end_postfix => "#{@eol_char||''}",
405
+ :header => wdiff_header(),
406
+ :footer => wdiff_footer(),
407
+ :start_common => '',
408
+ :end_common => '',
409
+ :start_del => '[-',
410
+ :end_del => '-]',
411
+ :start_add => '{+',
412
+ :end_add => '+}',
413
+ :start_before_change => '[-',
414
+ :end_before_change => '-]',
415
+ :start_after_change => '{+',
416
+ :end_after_change => '+}'}
417
+ end
418
+ def to_wdiff(overriding_opts = nil, headfoot = true)
419
+ tags = wdiff_tags()
420
+ tags.update(overriding_opts) if overriding_opts
421
+ apply_style(tags)
422
+ end
423
+ def to_wdiff_digest(overriding_opts = nil, headfoot = true)
424
+ tags = wdiff_tags()
425
+ tags.update(overriding_opts) if overriding_opts
426
+ apply_style_digest(tags, headfoot)
427
+ end
428
+
429
+ # user defined markup
430
+ def user_header(); []; end
431
+ def user_footer(); []; end
432
+ UserEscapeDic = {'ThisRandomString' => 'ThisRandomString'}
433
+ UserEscapePat = /(\r\n|#{UserEscapeDic.keys.collect{|k|Regexp.quote(k)}.join('|')})/m
434
+ def user_tags()
435
+ {:outside_escape_dic => UserEscapeDic,
436
+ :outside_escape_pat => UserEscapePat,
437
+ :inside_escape_dic => UserEscapeDic,
438
+ :inside_escape_pat => UserEscapePat,
439
+ :start_digest_body => '',
440
+ :end_digest_body => '',
441
+ :start_entry => '',
442
+ :end_entry => '',
443
+ :start_position => '',
444
+ :end_position => ' ',
445
+ :start_prefix => '',
446
+ :end_prefix => '',
447
+ :start_postfix => '',
448
+ :end_postfix => '',
449
+ :header => user_header(),
450
+ :footer => user_footer(),
451
+ :start_common => '',
452
+ :end_common => '',
453
+ :start_del => '',
454
+ :end_del => '',
455
+ :start_add => '',
456
+ :end_add => '',
457
+ :start_before_change => '',
458
+ :end_before_change => '',
459
+ :start_after_change => '',
460
+ :end_after_change => ''}
461
+ end
462
+ def to_user(overriding_opts = nil, headfoot = true)
463
+ tags = user_tags()
464
+ tags.update(overriding_opts) if overriding_opts
465
+ apply_style(tags, headfoot)
466
+ end
467
+ def to_user_digest(overriding_opts = nil, headfoot = true)
468
+ tags = user_tags()
469
+ tags.update(overriding_opts) if overriding_opts
470
+ apply_style_digest(tags, headfoot)
471
+ end
472
+
473
+ def to_debug()
474
+ end
475
+
476
+ end