diff_match_patch_es 2.0.0

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.
@@ -0,0 +1,551 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DiffMatchPatchES
4
+ # One patch operation.
5
+ class Patch
6
+ attr_accessor :diffs, :start1, :start2, :length1, :length2
7
+
8
+ def initialize
9
+ @diffs = []
10
+ @start1 = nil
11
+ @start2 = nil
12
+ @length1 = 0
13
+ @length2 = 0
14
+ end
15
+
16
+ # Emulate GNU diff's format.
17
+ # Header: @@ -382,8 +481,9 @@
18
+ # Indices are printed as 1-based, not 0-based.
19
+ def to_s
20
+ coords1 = if length1.zero?
21
+ "#{start1},0"
22
+ elsif length1 == 1
23
+ (start1 + 1).to_s
24
+ else
25
+ "#{start1 + 1},#{length1}"
26
+ end
27
+ coords2 = if length2.zero?
28
+ "#{start2},0"
29
+ elsif length2 == 1
30
+ (start2 + 1).to_s
31
+ else
32
+ "#{start2 + 1},#{length2}"
33
+ end
34
+
35
+ text = +"@@ -#{coords1} +#{coords2} @@\n"
36
+ # Escape the body of the patch with %xx notation.
37
+ diffs.each do |(op, data)|
38
+ sign = case op
39
+ when DIFF_INSERT then '+'
40
+ when DIFF_DELETE then '-'
41
+ when DIFF_EQUAL then ' '
42
+ end
43
+ text << sign << JsString.encode_uri(data) << "\n"
44
+ end
45
+ text.gsub('%20', ' ')
46
+ end
47
+
48
+ def ==(other)
49
+ other.is_a?(Patch) &&
50
+ diffs == other.diffs &&
51
+ start1 == other.start1 &&
52
+ start2 == other.start2 &&
53
+ length1 == other.length1 &&
54
+ length2 == other.length2
55
+ end
56
+ end
57
+
58
+ module_function
59
+
60
+ def create_patch
61
+ Patch.new
62
+ end
63
+
64
+ # Increase the context until it is unique,
65
+ # but don't let the pattern expand beyond match_max_bits.
66
+ def patch_add_context(patch, text, options)
67
+ return if text.empty?
68
+
69
+ raise Error, 'patch not initialized' if patch.start2.nil?
70
+
71
+ resolved = resolve_options(options)
72
+ match_max_bits = resolved.match_max_bits
73
+ patch_margin = resolved.patch_margin
74
+
75
+ pattern = JsString.substring(text, patch.start2, patch.start2 + patch.length1)
76
+ padding = 0
77
+
78
+ # Look for the first and last matches of pattern in text. If two
79
+ # different matches are found, increase the pattern length.
80
+ while JsString.index_of(text, pattern) != JsString.last_index_of(text, pattern, text.length) &&
81
+ pattern.length < match_max_bits - patch_margin - patch_margin
82
+ padding += patch_margin
83
+ pattern = JsString.substring(text, patch.start2 - padding, patch.start2 + patch.length1 + padding)
84
+ end
85
+ # Add one chunk for good luck.
86
+ padding += patch_margin
87
+
88
+ # Add the prefix.
89
+ prefix = JsString.substring(text, patch.start2 - padding, patch.start2)
90
+ patch.diffs.unshift(create_diff(DIFF_EQUAL, prefix)) unless prefix.empty?
91
+
92
+ # Add the suffix.
93
+ suffix = JsString.substring(text, patch.start2 + patch.length1, patch.start2 + patch.length1 + padding)
94
+ patch.diffs.push(create_diff(DIFF_EQUAL, suffix)) unless suffix.empty?
95
+
96
+ # Roll back the start points.
97
+ patch.start1 -= prefix.length
98
+ patch.start2 -= prefix.length
99
+ # Extend the lengths.
100
+ patch.length1 += prefix.length + suffix.length
101
+ patch.length2 += prefix.length + suffix.length
102
+ nil
103
+ end
104
+
105
+ # Compute a list of patches to turn text1 into text2.
106
+ # Use diffs if provided, otherwise compute it ourselves.
107
+ # There are four ways to call this function, depending on what data is
108
+ # available to the caller:
109
+ # Method 1: a = text1, b = text2
110
+ # Method 2: a = diffs
111
+ # Method 3 (optimal): a = text1, b = diffs
112
+ # Method 4 (deprecated, use method 3): a = text1, b = text2, c = diffs
113
+ def patch_make(a, opt_b = nil, opt_c = nil, options = nil)
114
+ resolved = resolve_options(options)
115
+
116
+ if a.is_a?(String) && opt_b.is_a?(String) && opt_c.nil?
117
+ # Method 1: text1, text2
118
+ # Compute diffs from text1 and text2.
119
+ text1 = a
120
+ diffs = diff_main(text1, opt_b, resolved, true)
121
+ if diffs.length > 2
122
+ diff_cleanup_semantic(diffs)
123
+ diff_cleanup_efficiency(diffs, resolved)
124
+ end
125
+ elsif a.is_a?(Array) && opt_b.nil? && opt_c.nil?
126
+ # Method 2: diffs
127
+ # Compute text1 from diffs.
128
+ diffs = a
129
+ text1 = diff_text1(diffs)
130
+ elsif a.is_a?(String) && opt_b.is_a?(Array) && opt_c.nil?
131
+ # Method 3: text1, diffs
132
+ text1 = a
133
+ diffs = opt_b
134
+ elsif a.is_a?(String) && opt_b.is_a?(String) && opt_c.is_a?(Array)
135
+ # Method 4: text1, text2, diffs
136
+ # text2 is not used.
137
+ text1 = a
138
+ diffs = opt_c
139
+ else
140
+ raise Error, 'Unknown call format to patch_make.'
141
+ end
142
+
143
+ return [] if diffs.empty? # Get rid of the null case.
144
+
145
+ patches = []
146
+ patch = Patch.new
147
+ patch_diff_length = 0 # Keeping our own length var is faster in JS.
148
+ char_count1 = 0 # Number of characters into the text1 string.
149
+ char_count2 = 0 # Number of characters into the text2 string.
150
+ # Start with text1 (prepatch_text) and apply the diffs until we arrive at
151
+ # text2 (postpatch_text). We recreate the patches one by one to determine
152
+ # context info.
153
+ prepatch_text = text1
154
+ postpatch_text = text1
155
+ diffs.each_with_index do |diff, x|
156
+ diff_type = diff[0]
157
+ diff_text = diff[1]
158
+
159
+ if patch_diff_length.zero? && diff_type != DIFF_EQUAL
160
+ # A new patch starts here.
161
+ patch.start1 = char_count1
162
+ patch.start2 = char_count2
163
+ end
164
+
165
+ case diff_type
166
+ when DIFF_INSERT
167
+ patch.diffs[patch_diff_length] = diff
168
+ patch_diff_length += 1
169
+ patch.length2 += diff_text.length
170
+ postpatch_text = JsString.substring(postpatch_text, 0, char_count2) + diff_text +
171
+ JsString.substring(postpatch_text, char_count2)
172
+ when DIFF_DELETE
173
+ patch.length1 += diff_text.length
174
+ patch.diffs[patch_diff_length] = diff
175
+ patch_diff_length += 1
176
+ postpatch_text = JsString.substring(postpatch_text, 0, char_count2) +
177
+ JsString.substring(postpatch_text, char_count2 + diff_text.length)
178
+ when DIFF_EQUAL
179
+ if diff_text.length <= 2 * resolved.patch_margin &&
180
+ patch_diff_length.positive? && diffs.length != x + 1
181
+ # Small equality inside a patch.
182
+ patch.diffs[patch_diff_length] = diff
183
+ patch_diff_length += 1
184
+ patch.length1 += diff_text.length
185
+ patch.length2 += diff_text.length
186
+ elsif diff_text.length >= 2 * resolved.patch_margin
187
+ # Time for a new patch.
188
+ if patch_diff_length.positive?
189
+ patch_add_context(patch, prepatch_text, resolved)
190
+ patches.push(patch)
191
+ patch = Patch.new
192
+ patch_diff_length = 0
193
+ # Unlike Unidiff, our patch lists have a rolling context.
194
+ # https://github.com/google/diff-match-patch/wiki/Unidiff
195
+ # Update prepatch text & pos to reflect the application of the
196
+ # just completed patch.
197
+ prepatch_text = postpatch_text
198
+ char_count1 = char_count2
199
+ end
200
+ end
201
+ end
202
+
203
+ # Update the current character count.
204
+ char_count1 += diff_text.length if diff_type != DIFF_INSERT
205
+ char_count2 += diff_text.length if diff_type != DIFF_DELETE
206
+ end
207
+ # Pick up the leftover patch if not empty.
208
+ if patch_diff_length.positive?
209
+ patch_add_context(patch, prepatch_text, resolved)
210
+ patches.push(patch)
211
+ end
212
+
213
+ patches
214
+ end
215
+
216
+ # Given an array of patches, return another array that is identical.
217
+ def patch_deep_copy(patches)
218
+ patches.map do |patch|
219
+ patch_copy = Patch.new
220
+ patch_copy.diffs = patch.diffs.map { |(op, text)| create_diff(op, text) }
221
+ patch_copy.start1 = patch.start1
222
+ patch_copy.start2 = patch.start2
223
+ patch_copy.length1 = patch.length1
224
+ patch_copy.length2 = patch.length2
225
+ patch_copy
226
+ end
227
+ end
228
+
229
+ # Merge a set of patches onto the text. Return a patched text, as well
230
+ # as a list of true/false values indicating which patches were applied.
231
+ def patch_apply(patches, text, options = nil)
232
+ return [text, []] if patches.empty?
233
+
234
+ # Deep copy the patches so that no changes are made to originals.
235
+ patches = patch_deep_copy(patches)
236
+
237
+ resolved = resolve_options(options)
238
+ null_padding = patch_add_padding(patches, resolved)
239
+ text = null_padding + text + null_padding
240
+
241
+ patch_split_max(patches, resolved)
242
+ # delta keeps track of the offset between the expected and actual
243
+ # location of the previous patch. If there are patches expected at
244
+ # positions 10 and 20, but the first patch was found at 12, delta is 2
245
+ # and the second patch has an effective expected position of 22.
246
+ delta = 0
247
+ results = []
248
+ patches.each_with_index do |patch, x|
249
+ expected_loc = patch.start2 + delta
250
+ text1 = diff_text1(patch.diffs)
251
+ end_loc = -1
252
+ if text1.length > resolved.match_max_bits
253
+ # patch_split_max will only provide an oversized pattern in the case
254
+ # of a monster delete.
255
+ start_loc = match_main(
256
+ text,
257
+ JsString.substring(text1, 0, resolved.match_max_bits),
258
+ expected_loc,
259
+ resolved,
260
+ )
261
+ if start_loc != -1
262
+ end_loc = match_main(
263
+ text,
264
+ JsString.substring(text1, text1.length - resolved.match_max_bits),
265
+ expected_loc + text1.length - resolved.match_max_bits,
266
+ resolved,
267
+ )
268
+ if end_loc == -1 || start_loc >= end_loc
269
+ # Can't find valid trailing context. Drop this patch.
270
+ start_loc = -1
271
+ end
272
+ end
273
+ else
274
+ start_loc = match_main(text, text1, expected_loc, resolved)
275
+ end
276
+ if start_loc == -1
277
+ # No match found. :(
278
+ results[x] = false
279
+ # Subtract the delta for this failed patch from subsequent patches.
280
+ delta -= patch.length2 - patch.length1
281
+ else
282
+ # Found a match. :)
283
+ results[x] = true
284
+ delta = start_loc - expected_loc
285
+ text2 = if end_loc == -1
286
+ JsString.substring(text, start_loc, start_loc + text1.length)
287
+ else
288
+ JsString.substring(text, start_loc, end_loc + resolved.match_max_bits)
289
+ end
290
+
291
+ if text1 == text2
292
+ # Perfect match, just shove the replacement text in.
293
+ text = JsString.substring(text, 0, start_loc) +
294
+ diff_text2(patch.diffs) +
295
+ JsString.substring(text, start_loc + text1.length)
296
+ else
297
+ # Imperfect match. Run a diff to get a framework of equivalent
298
+ # indices.
299
+ diffs = diff_main(text1, text2, resolved, false)
300
+ if text1.length > resolved.match_max_bits &&
301
+ diff_levenshtein(diffs).fdiv(text1.length) > resolved.patch_delete_threshold
302
+ # The end points match, but the content is unacceptably bad.
303
+ results[x] = false
304
+ else
305
+ diff_cleanup_semantic_lossless(diffs)
306
+ index1 = 0
307
+ index2 = 0
308
+ patch.diffs.each do |mod|
309
+ index2 = diff_x_index(diffs, index1) if mod[0] != DIFF_EQUAL
310
+ if mod[0] == DIFF_INSERT # Insertion
311
+ text = JsString.substring(text, 0, start_loc + index2) + mod[1] +
312
+ JsString.substring(text, start_loc + index2)
313
+ elsif mod[0] == DIFF_DELETE # Deletion
314
+ text = JsString.substring(text, 0, start_loc + index2) +
315
+ JsString.substring(text, start_loc + diff_x_index(diffs, index1 + mod[1].length))
316
+ end
317
+ index1 += mod[1].length if mod[0] != DIFF_DELETE
318
+ end
319
+ end
320
+ end
321
+ end
322
+ end
323
+ # Strip the padding off.
324
+ text = JsString.substring(text, null_padding.length, text.length - null_padding.length)
325
+ [text, results]
326
+ end
327
+
328
+ # Add some padding on text start and end so that edges can match something.
329
+ # Intended to be called only from within patch_apply.
330
+ # Returns the padding string added to each side.
331
+ def patch_add_padding(patches, options = nil)
332
+ padding_length = resolve_options(options).patch_margin
333
+ null_padding = +''
334
+ (1..padding_length).each { |x| null_padding << x.chr(Encoding::UTF_8) }
335
+
336
+ # Bump all the patches forward.
337
+ patches.each do |patch|
338
+ patch.start1 += padding_length
339
+ patch.start2 += padding_length
340
+ end
341
+
342
+ # Add some padding on start of first diff.
343
+ patch = patches[0]
344
+ diffs = patch.diffs
345
+ if diffs.empty? || diffs[0][0] != DIFF_EQUAL
346
+ # Add null_padding equality.
347
+ diffs.unshift(create_diff(DIFF_EQUAL, null_padding))
348
+ patch.start1 -= padding_length # Should be 0.
349
+ patch.start2 -= padding_length # Should be 0.
350
+ patch.length1 += padding_length
351
+ patch.length2 += padding_length
352
+ elsif padding_length > diffs[0][1].length
353
+ # Grow first equality.
354
+ extra_length = padding_length - diffs[0][1].length
355
+ diffs[0][1] = JsString.substring(null_padding, diffs[0][1].length) + diffs[0][1]
356
+ patch.start1 -= extra_length
357
+ patch.start2 -= extra_length
358
+ patch.length1 += extra_length
359
+ patch.length2 += extra_length
360
+ end
361
+
362
+ # Add some padding on end of last diff.
363
+ patch = patches[patches.length - 1]
364
+ diffs = patch.diffs
365
+ if diffs.empty? || diffs[diffs.length - 1][0] != DIFF_EQUAL
366
+ # Add null_padding equality.
367
+ diffs.push(create_diff(DIFF_EQUAL, null_padding))
368
+ patch.length1 += padding_length
369
+ patch.length2 += padding_length
370
+ elsif padding_length > diffs[diffs.length - 1][1].length
371
+ # Grow last equality.
372
+ extra_length = padding_length - diffs[diffs.length - 1][1].length
373
+ diffs[diffs.length - 1][1] += JsString.substring(null_padding, 0, extra_length)
374
+ patch.length1 += extra_length
375
+ patch.length2 += extra_length
376
+ end
377
+
378
+ null_padding
379
+ end
380
+
381
+ # Look through the patches and break up any which are longer than the
382
+ # maximum limit of the match algorithm.
383
+ # Intended to be called only from within patch_apply.
384
+ def patch_split_max(patches, options = nil)
385
+ resolved = resolve_options(options)
386
+ x = 0
387
+ while x < patches.length
388
+ if patches[x].length1 <= resolved.match_max_bits
389
+ x += 1
390
+ next
391
+ end
392
+
393
+ bigpatch = patches[x]
394
+ # Remove the big old patch.
395
+ patches.delete_at(x)
396
+ x -= 1
397
+ start1 = bigpatch.start1
398
+ start2 = bigpatch.start2
399
+ precontext = ''
400
+ until bigpatch.diffs.empty?
401
+ # Create one of several smaller patches.
402
+ patch = Patch.new
403
+ empty = true
404
+ patch.start1 = start1 - precontext.length
405
+ patch.start2 = start2 - precontext.length
406
+ unless precontext.empty?
407
+ patch.length1 = patch.length2 = precontext.length
408
+ patch.diffs.push(create_diff(DIFF_EQUAL, precontext))
409
+ end
410
+ while !bigpatch.diffs.empty? &&
411
+ patch.length1 < resolved.match_max_bits - resolved.patch_margin
412
+ diff_type = bigpatch.diffs[0][0]
413
+ diff_text = bigpatch.diffs[0][1]
414
+ if diff_type == DIFF_INSERT
415
+ # Insertions are harmless.
416
+ patch.length2 += diff_text.length
417
+ start2 += diff_text.length
418
+ patch.diffs.push(bigpatch.diffs.shift)
419
+ empty = false
420
+ elsif diff_type == DIFF_DELETE && patch.diffs.length == 1 &&
421
+ patch.diffs[0][0] == DIFF_EQUAL &&
422
+ diff_text.length > 2 * resolved.match_max_bits
423
+ # This is a large deletion. Let it pass in one chunk.
424
+ patch.length1 += diff_text.length
425
+ start1 += diff_text.length
426
+ empty = false
427
+ patch.diffs.push(create_diff(diff_type, diff_text))
428
+ bigpatch.diffs.shift
429
+ else
430
+ # Deletion or equality. Only take as much as we can stomach.
431
+ diff_text = JsString.substring(
432
+ diff_text, 0, resolved.match_max_bits - patch.length1 - resolved.patch_margin
433
+ )
434
+ patch.length1 += diff_text.length
435
+ start1 += diff_text.length
436
+ if diff_type == DIFF_EQUAL
437
+ patch.length2 += diff_text.length
438
+ start2 += diff_text.length
439
+ else
440
+ empty = false
441
+ end
442
+ patch.diffs.push(create_diff(diff_type, diff_text))
443
+ if diff_text == bigpatch.diffs[0][1]
444
+ bigpatch.diffs.shift
445
+ else
446
+ bigpatch.diffs[0][1] = JsString.substring(bigpatch.diffs[0][1], diff_text.length)
447
+ end
448
+ end
449
+ end
450
+ # Compute the head context for the next patch.
451
+ precontext = diff_text2(patch.diffs)
452
+ precontext = JsString.substring(precontext, precontext.length - resolved.patch_margin)
453
+ # Append the end context for this patch.
454
+ postcontext = JsString.substring(diff_text1(bigpatch.diffs), 0, resolved.patch_margin)
455
+ unless postcontext.empty?
456
+ patch.length1 += postcontext.length
457
+ patch.length2 += postcontext.length
458
+ if !patch.diffs.empty? && patch.diffs[patch.diffs.length - 1][0] == DIFF_EQUAL
459
+ patch.diffs[patch.diffs.length - 1][1] += postcontext
460
+ else
461
+ patch.diffs.push(create_diff(DIFF_EQUAL, postcontext))
462
+ end
463
+ end
464
+ unless empty
465
+ x += 1
466
+ patches.insert(x, patch)
467
+ end
468
+ end
469
+ x += 1
470
+ end
471
+ nil
472
+ end
473
+
474
+ # Take a list of patches and return a textual representation.
475
+ def patch_to_text(patches)
476
+ patches.map(&:to_s).join
477
+ end
478
+
479
+ # Parse a textual representation of patches and return a list of Patch
480
+ # objects.
481
+ def patch_from_text(textline)
482
+ patches = []
483
+ return patches if textline.nil? || textline.empty?
484
+
485
+ text = textline.split("\n", -1)
486
+ text_pointer = 0
487
+ patch_header = /\A@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@\z/
488
+ while text_pointer < text.length
489
+ m = patch_header.match(text[text_pointer])
490
+ raise Error, "Invalid patch string: #{text[text_pointer]}" unless m
491
+
492
+ patch = Patch.new
493
+ patches.push(patch)
494
+ patch.start1 = m[1].to_i
495
+ if m[2] == ''
496
+ patch.start1 -= 1
497
+ patch.length1 = 1
498
+ elsif m[2] == '0'
499
+ patch.length1 = 0
500
+ else
501
+ patch.start1 -= 1
502
+ patch.length1 = m[2].to_i
503
+ end
504
+
505
+ patch.start2 = m[3].to_i
506
+ if m[4] == ''
507
+ patch.start2 -= 1
508
+ patch.length2 = 1
509
+ elsif m[4] == '0'
510
+ patch.length2 = 0
511
+ else
512
+ patch.start2 -= 1
513
+ patch.length2 = m[4].to_i
514
+ end
515
+ text_pointer += 1
516
+
517
+ while text_pointer < text.length
518
+ sign = JsString.char_at(text[text_pointer], 0)
519
+ line = ''
520
+ begin
521
+ line = JsString.decode_uri(JsString.substring(text[text_pointer], 1))
522
+ rescue JsString::UriError
523
+ # Malformed URI sequence. (The reference implementation reports the
524
+ # pre-assignment value of `line`, which is always empty.)
525
+ raise Error, 'Illegal escape in patch_fromText: '
526
+ end
527
+ case sign
528
+ when '-'
529
+ # Deletion.
530
+ patch.diffs.push(create_diff(DIFF_DELETE, line))
531
+ when '+'
532
+ # Insertion.
533
+ patch.diffs.push(create_diff(DIFF_INSERT, line))
534
+ when ' '
535
+ # Minor equality.
536
+ patch.diffs.push(create_diff(DIFF_EQUAL, line))
537
+ when '@'
538
+ # Start of next patch.
539
+ break
540
+ when ''
541
+ # Blank line? Whatever.
542
+ else
543
+ # WTF?
544
+ raise Error, "Invalid patch mode \"#{sign}\" in: #{line}"
545
+ end
546
+ text_pointer += 1
547
+ end
548
+ end
549
+ patches
550
+ end
551
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DiffMatchPatchES
4
+ VERSION = '2.0.0'
5
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Diff Match and Patch
4
+ # Copyright 2018 The diff-match-patch Authors.
5
+ # https://github.com/google/diff-match-patch
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ # Ruby port of diff-match-patch-es (the TypeScript/ESM rewrite by Anthony Fu
20
+ # of Neil Fraser's diff-match-patch), producing identical results.
21
+ #
22
+ # The API is a set of stateless module functions; nothing mutates shared
23
+ # state, DEFAULT_OPTIONS is frozen, and every call operates only on its
24
+ # arguments and locals, so the module is safe to use from multiple threads
25
+ # (and Ractors) concurrently.
26
+ module DiffMatchPatchES
27
+ class Error < StandardError; end
28
+ end
29
+
30
+ require_relative 'diff_match_patch_es/version'
31
+ require_relative 'diff_match_patch_es/js_string'
32
+ require_relative 'diff_match_patch_es/options'
33
+ require_relative 'diff_match_patch_es/diff'
34
+ require_relative 'diff_match_patch_es/match'
35
+ require_relative 'diff_match_patch_es/patch'
36
+
37
+ module DiffMatchPatchES
38
+ module_function
39
+
40
+ def diff(text1, text2, options = nil, checklines = true, deadline = nil)
41
+ diff_main(text1, text2, options, checklines, deadline)
42
+ end
43
+
44
+ def match(text, pattern, loc, options = nil)
45
+ match_main(text, pattern, loc, options)
46
+ end
47
+
48
+ def patch(a, opt_b = nil, opt_c = nil, options = nil)
49
+ patch_make(a, opt_b, opt_c, options)
50
+ end
51
+ end