1use crate::patch::chunk::Chunk;
3use crate::patch::delta::Delta;
4use crate::patch::delta_type::DeltaType;
5use crate::patch::patch::Patch;
6use crate::text::delta_merge::delta_merge_utils::DeltaMergeUtils;
7use crate::text::delta_merge::inline_delta_merge_info::InlineDeltaMergeInfo;
8use crate::text::diff_row::{DiffRow, Tag};
9use crate::text::string_utils;
10use lazy_static::lazy_static;
11use regex::Regex;
12use std::sync::Arc;
13
14lazy_static! {
15 pub static ref SPLIT_BY_WORD_PATTERN: Regex =
16 Regex::new(r"\s+|[,.\[\](){}/\\*+\-#<>;:&']+").unwrap();
17 static ref WHITESPACE_RE: Regex = Regex::new(r"\s+").unwrap();
18 pub static ref WHITESPACE_EQUALITIES_MERGER: InlineDeltaMergerFn =
19 whitespace_equalities_merger();
20}
21
22pub type EqualizerFn = Arc<dyn Fn(&str, &str) -> bool + Send + Sync>;
24pub type TagGeneratorFn = Arc<dyn Fn(Tag, bool) -> String + Send + Sync>;
25pub type StringTransformFn = Arc<dyn Fn(&str) -> String + Send + Sync>;
26pub type SplitterFn = Arc<dyn Fn(&str) -> Vec<String> + Send + Sync>;
27pub type InlineDeltaMergerFn =
28 Arc<dyn Fn(&InlineDeltaMergeInfo<String>) -> Vec<Delta<String>> + Send + Sync>;
29
30pub fn adjust_whitespace(raw: &str) -> String {
32 WHITESPACE_RE.replace_all(raw.trim(), " ").to_string()
33}
34
35pub fn default_equalizer() -> EqualizerFn {
37 Arc::new(|orig, rev| orig == rev)
38}
39
40pub fn ignore_whitespace_equalizer() -> EqualizerFn {
42 Arc::new(|orig, rev| adjust_whitespace(orig) == adjust_whitespace(rev))
43}
44
45pub fn html_line_normalizer() -> StringTransformFn {
47 Arc::new(string_utils::normalize)
48}
49
50pub fn splitter_by_character() -> SplitterFn {
52 Arc::new(|line| line.chars().map(|c| c.to_string()).collect())
53}
54
55pub fn splitter_by_word() -> SplitterFn {
57 Arc::new(|line| split_string_preserve_delimiter(line, &SPLIT_BY_WORD_PATTERN))
58}
59
60pub fn default_inline_delta_merger() -> InlineDeltaMergerFn {
62 Arc::new(|info: &InlineDeltaMergeInfo<String>| info.deltas().to_vec())
63}
64
65pub fn whitespace_equalities_merger() -> InlineDeltaMergerFn {
67 Arc::new(move |info| {
68 DeltaMergeUtils::merge_inline_deltas(info, move |equalities: &[String]| {
69 equalities
70 .iter()
71 .all(|s| WHITESPACE_RE.replace_all(s, "").is_empty())
72 })
73 })
74}
75
76pub fn split_string_preserve_delimiter(str_input: &str, pattern: &Regex) -> Vec<String> {
78 let mut list = Vec::new();
79 let mut pos = 0;
80
81 for mat in pattern.find_iter(str_input) {
82 if pos < mat.start() {
83 list.push(str_input[pos..mat.start()].to_string());
84 }
85 list.push(mat.as_str().to_string());
86 pos = mat.end();
87 }
88
89 if pos < str_input.len() {
90 list.push(str_input[pos..].to_string());
91 }
92
93 list
94}
95
96pub fn wrap_in_tag(
98 sequence: &mut Vec<String>,
99 start_position: usize,
100 end_position: usize,
101 tag: Tag,
102 tag_generator: &TagGeneratorFn,
103 process_diffs: Option<&StringTransformFn>,
104 replace_linefeed_with_space: bool,
105) {
106 if start_position >= sequence.len() && end_position >= sequence.len() {
107 return;
108 }
109
110 let mut end_pos = end_position.min(sequence.len());
111
112 while end_pos >= start_position {
113 while end_pos > start_position {
114 if sequence[end_pos - 1] != "\n" {
115 break;
116 } else if replace_linefeed_with_space {
117 sequence[end_pos - 1] = " ".to_string();
118 break;
119 }
120 end_pos -= 1;
121 }
122
123 if end_pos == start_position {
124 break;
125 }
126
127 sequence.insert(end_pos, tag_generator(tag, false));
128 if let Some(proc) = process_diffs {
129 sequence[end_pos - 1] = proc(&sequence[end_pos - 1]);
130 }
131 end_pos -= 1;
132
133 while end_pos > start_position {
134 if sequence[end_pos - 1] == "\n" {
135 if replace_linefeed_with_space {
136 sequence[end_pos - 1] = " ".to_string();
137 } else {
138 break;
139 }
140 }
141 if let Some(proc) = process_diffs {
142 sequence[end_pos - 1] = proc(&sequence[end_pos - 1]);
143 }
144 end_pos -= 1;
145 }
146
147 sequence.insert(end_pos, tag_generator(tag, true));
148 if end_pos == 0 {
149 break;
150 }
151 end_pos -= 1;
152 }
153}
154
155#[derive(Clone)]
157pub struct DiffRowGenerator {
158 column_width: usize,
159 equalizer: EqualizerFn,
160 ignore_white_spaces: bool,
161 inline_diff_splitter: SplitterFn,
162 merge_original_revised: bool,
163 old_tag: TagGeneratorFn,
164 new_tag: TagGeneratorFn,
165 report_lines_unchanged: bool,
166 line_normalizer: StringTransformFn,
167 process_diffs: Option<StringTransformFn>,
168 inline_delta_merger: InlineDeltaMergerFn,
169 equality_processor: Option<StringTransformFn>,
170 show_inline_diffs: bool,
171 replace_original_linefeed_in_changes_with_spaces: bool,
172 decompress_deltas: bool,
173}
174
175impl DiffRowGenerator {
176 pub fn create() -> Builder {
178 Builder::new()
179 }
180
181 pub fn split_string_preserve_delimiter(str_input: &str, pattern: &Regex) -> Vec<String> {
183 split_string_preserve_delimiter(str_input, pattern)
184 }
185
186 pub fn ignore_white_spaces(&self) -> bool {
188 self.ignore_white_spaces
189 }
190
191 pub fn generate_diff_rows(&self, original: &[String], revised: &[String]) -> Vec<DiffRow> {
193 let mut patch = crate::diff_utils::DiffUtils::diff_with_equalizer(
194 original,
195 revised,
196 Some({
197 let eq = self.equalizer.clone();
198 move |a: &String, b: &String| eq(a, b)
199 }),
200 );
201 self.generate_diff_rows_from_patch(original, &mut patch)
202 }
203
204 pub fn generate_diff_rows_from_patch(
206 &self,
207 original: &[String],
208 patch: &mut Patch<String>,
209 ) -> Vec<DiffRow> {
210 let mut diff_rows = Vec::new();
211 let mut end_pos = 0;
212 let delta_list = Self::normalize_patch_deltas(patch.deltas().to_vec());
213
214 if self.decompress_deltas {
215 for original_delta in &delta_list {
216 for delta in self.decompress_deltas_internal(original_delta) {
217 end_pos = self.transform_delta_into_diff_row(
218 original,
219 end_pos,
220 &mut diff_rows,
221 &delta,
222 );
223 }
224 }
225 } else {
226 for delta in &delta_list {
227 end_pos =
228 self.transform_delta_into_diff_row(original, end_pos, &mut diff_rows, delta);
229 }
230 }
231
232 if end_pos < original.len() {
233 for line in &original[end_pos..] {
234 let processed = self.process_equalities(line);
235 diff_rows.push(self.build_diff_row(Tag::Equal, &processed, &processed));
236 }
237 }
238
239 diff_rows
240 }
241
242 fn normalize_patch_deltas(deltas: Vec<Delta<String>>) -> Vec<Delta<String>> {
243 let mut normalized = Vec::with_capacity(deltas.len());
244 let mut index = 0;
245
246 while index < deltas.len() {
247 if index + 1 < deltas.len() {
248 let current = &deltas[index];
249 let next = &deltas[index + 1];
250
251 let same_source_position = current.source().position() == next.source().position();
252
253 let should_merge = same_source_position
254 && ((current.delta_type() == DeltaType::Delete
255 && next.delta_type() == DeltaType::Insert)
256 || (current.delta_type() == DeltaType::Insert
257 && next.delta_type() == DeltaType::Delete));
258
259 if should_merge {
260 let merged_source = if current.delta_type() == DeltaType::Delete {
261 current.source().clone()
262 } else {
263 next.source().clone()
264 };
265 let merged_target = if current.delta_type() == DeltaType::Insert {
266 current.target().clone()
267 } else {
268 next.target().clone()
269 };
270
271 normalized.push(Delta::new(DeltaType::Change, merged_source, merged_target));
272 index += 2;
273 continue;
274 }
275 }
276
277 normalized.push(deltas[index].clone());
278 index += 1;
279 }
280
281 normalized
282 }
283
284 fn transform_delta_into_diff_row(
285 &self,
286 original: &[String],
287 end_pos: usize,
288 diff_rows: &mut Vec<DiffRow>,
289 delta: &Delta<String>,
290 ) -> usize {
291 let orig = delta.source();
292 let rev = delta.target();
293
294 let target_pos = orig.position().min(original.len());
295 if end_pos < target_pos {
296 for line in &original[end_pos..target_pos] {
297 let processed = self.process_equalities(line);
298 diff_rows.push(self.build_diff_row(Tag::Equal, &processed, &processed));
299 }
300 }
301
302 match delta.delta_type() {
303 DeltaType::Insert => {
304 for line in rev.lines() {
305 diff_rows.push(self.build_diff_row(Tag::Insert, "", line));
306 }
307 }
308 DeltaType::Delete => {
309 for line in orig.lines() {
310 diff_rows.push(self.build_diff_row(Tag::Delete, line, ""));
311 }
312 }
313 _ => {
314 if self.show_inline_diffs {
315 diff_rows.extend(self.generate_inline_diffs(delta));
316 } else {
317 let max_size = orig.lines().len().max(rev.lines().len());
318 for j in 0..max_size {
319 let orig_line = orig.lines().get(j).map(|s| s.as_str()).unwrap_or("");
320 let rev_line = rev.lines().get(j).map(|s| s.as_str()).unwrap_or("");
321 diff_rows.push(self.build_diff_row(Tag::Change, orig_line, rev_line));
322 }
323 }
324 }
325 }
326
327 orig.position() + orig.len()
328 }
329
330 fn decompress_deltas_internal(&self, delta: &Delta<String>) -> Vec<Delta<String>> {
331 if delta.delta_type() == DeltaType::Change && delta.source().len() != delta.target().len() {
332 let mut deltas = Vec::new();
333 let min_size = delta.source().len().min(delta.target().len());
334 let orig = delta.source();
335 let rev = delta.target();
336
337 if min_size > 0 {
338 let orig_sub = orig.lines()[..min_size].to_vec();
339 let rev_sub = rev.lines()[..min_size].to_vec();
340
341 deltas.push(Delta::new(
342 DeltaType::Change,
343 Chunk::new(orig.position(), orig_sub, None),
344 Chunk::new(rev.position(), rev_sub, None),
345 ));
346 }
347
348 if orig.lines().len() < rev.lines().len() {
349 deltas.push(Delta::new(
350 DeltaType::Insert,
351 Chunk::new(orig.position() + min_size, Vec::new(), None),
352 Chunk::new(
353 rev.position() + min_size,
354 rev.lines()[min_size..].to_vec(),
355 None,
356 ),
357 ));
358 } else if orig.lines().len() > rev.lines().len() {
359 deltas.push(Delta::new(
360 DeltaType::Delete,
361 Chunk::new(
362 orig.position() + min_size,
363 orig.lines()[min_size..].to_vec(),
364 None,
365 ),
366 Chunk::new(rev.position() + min_size, Vec::new(), None),
367 ));
368 }
369
370 deltas
371 } else {
372 vec![delta.clone()]
373 }
374 }
375
376 fn build_diff_row(&self, tag_type: Tag, orgline: &str, newline: &str) -> DiffRow {
377 if self.report_lines_unchanged {
378 DiffRow::new(tag_type, orgline, newline)
379 } else {
380 let mut wrap_org = self.preprocess_line(orgline);
381 if Tag::Delete == tag_type && (self.merge_original_revised || self.show_inline_diffs) {
382 wrap_org = format!(
383 "{}{}{}",
384 (self.old_tag)(tag_type, true),
385 wrap_org,
386 (self.old_tag)(tag_type, false)
387 );
388 }
389
390 let mut wrap_new = self.preprocess_line(newline);
391 if Tag::Insert == tag_type {
392 if self.merge_original_revised {
393 wrap_org = format!(
394 "{}{}{}",
395 (self.new_tag)(tag_type, true),
396 wrap_new,
397 (self.new_tag)(tag_type, false)
398 );
399 } else if self.show_inline_diffs {
400 wrap_new = format!(
401 "{}{}{}",
402 (self.new_tag)(tag_type, true),
403 wrap_new,
404 (self.new_tag)(tag_type, false)
405 );
406 }
407 }
408
409 DiffRow::new(tag_type, wrap_org, wrap_new)
410 }
411 }
412
413 fn build_diff_row_without_normalizing(
414 &self,
415 tag_type: Tag,
416 orgline: &str,
417 newline: &str,
418 ) -> DiffRow {
419 DiffRow::new(
420 tag_type,
421 string_utils::wrap_text(orgline, self.column_width),
422 string_utils::wrap_text(newline, self.column_width),
423 )
424 }
425
426 pub fn normalize_lines(&self, list: &[String]) -> Vec<String> {
427 if self.report_lines_unchanged {
428 list.to_vec()
429 } else {
430 list.iter()
431 .map(|line| (self.line_normalizer)(line))
432 .collect()
433 }
434 }
435
436 fn generate_inline_diffs(&self, delta: &Delta<String>) -> Vec<DiffRow> {
437 let orig = self.normalize_lines(delta.source().lines());
438 let rev = self.normalize_lines(delta.target().lines());
439
440 let joined_orig = orig.join("\n");
441 let joined_rev = rev.join("\n");
442
443 let mut orig_list = (self.inline_diff_splitter)(&joined_orig);
444 let mut rev_list = (self.inline_diff_splitter)(&joined_rev);
445
446 let patch = crate::diff_utils::DiffUtils::diff_with_equalizer(
447 &orig_list,
448 &rev_list,
449 Some({
450 let eq = self.equalizer.clone();
451 move |a: &String, b: &String| eq(a, b)
452 }),
453 );
454 let original_inline_deltas = patch.deltas().to_vec();
455
456 let inline_merge_info =
457 InlineDeltaMergeInfo::new(original_inline_deltas, orig_list.clone(), rev_list.clone());
458 let mut inline_deltas: Vec<Delta<String>> = (self.inline_delta_merger)(&inline_merge_info);
459 inline_deltas.reverse();
460
461 for inline_delta in &inline_deltas {
462 let inline_orig = inline_delta.source();
463 let inline_rev = inline_delta.target();
464
465 match inline_delta.delta_type() {
466 DeltaType::Delete => {
467 wrap_in_tag(
468 &mut orig_list,
469 inline_orig.position(),
470 inline_orig.position() + inline_orig.len(),
471 Tag::Delete,
472 &self.old_tag,
473 self.process_diffs.as_ref(),
474 self.replace_original_linefeed_in_changes_with_spaces
475 && self.merge_original_revised,
476 );
477 }
478 DeltaType::Insert => {
479 if self.merge_original_revised {
480 let mut temp_rev = rev_list.clone();
481 let len_before = temp_rev.len();
482 wrap_in_tag(
483 &mut temp_rev,
484 inline_rev.position(),
485 inline_rev.position() + inline_rev.len(),
486 Tag::Insert,
487 &self.new_tag,
488 self.process_diffs.as_ref(),
489 false,
490 );
491 let tags_added = temp_rev.len() - len_before;
492 let insert_start = inline_rev.position().min(temp_rev.len());
493 let insert_end = (inline_rev.position() + inline_rev.len() + tags_added)
494 .min(temp_rev.len());
495 let sub = temp_rev[insert_start..insert_end].to_vec();
496 let orig_pos = inline_orig.position().min(orig_list.len());
497 orig_list.splice(orig_pos..orig_pos, sub);
498 } else {
499 wrap_in_tag(
500 &mut rev_list,
501 inline_rev.position(),
502 inline_rev.position() + inline_rev.len(),
503 Tag::Insert,
504 &self.new_tag,
505 self.process_diffs.as_ref(),
506 false,
507 );
508 }
509 }
510 DeltaType::Change => {
511 if self.merge_original_revised {
512 let mut temp_rev = rev_list.clone();
513 let rev_len_before = temp_rev.len();
514 wrap_in_tag(
515 &mut temp_rev,
516 inline_rev.position(),
517 inline_rev.position() + inline_rev.len(),
518 Tag::Change,
519 &self.new_tag,
520 self.process_diffs.as_ref(),
521 false,
522 );
523 let rev_tags_added = temp_rev.len() - rev_len_before;
524
525 let orig_len_before = orig_list.len();
526 wrap_in_tag(
527 &mut orig_list,
528 inline_orig.position(),
529 inline_orig.position() + inline_orig.len(),
530 Tag::Change,
531 &self.old_tag,
532 self.process_diffs.as_ref(),
533 self.replace_original_linefeed_in_changes_with_spaces
534 && self.merge_original_revised,
535 );
536 let orig_tags_added = orig_list.len() - orig_len_before;
537
538 let insert_start = inline_rev.position().min(temp_rev.len());
539 let insert_end =
540 (inline_rev.position() + inline_rev.len() + rev_tags_added)
541 .min(temp_rev.len());
542 let sub = temp_rev[insert_start..insert_end].to_vec();
543 let orig_pos =
544 (inline_orig.position() + inline_orig.len() + orig_tags_added)
545 .min(orig_list.len());
546 orig_list.splice(orig_pos..orig_pos, sub);
547 } else {
548 wrap_in_tag(
549 &mut rev_list,
550 inline_rev.position(),
551 inline_rev.position() + inline_rev.len(),
552 Tag::Change,
553 &self.new_tag,
554 self.process_diffs.as_ref(),
555 false,
556 );
557 wrap_in_tag(
558 &mut orig_list,
559 inline_orig.position(),
560 inline_orig.position() + inline_orig.len(),
561 Tag::Change,
562 &self.old_tag,
563 self.process_diffs.as_ref(),
564 self.replace_original_linefeed_in_changes_with_spaces
565 && self.merge_original_revised,
566 );
567 }
568 }
569 _ => {}
570 }
571 }
572
573 let orig_result: String = orig_list.concat();
574 let rev_result: String = rev_list.concat();
575
576 let mut original_split: Vec<String> =
577 orig_result.split('\n').map(|s| s.to_string()).collect();
578 let mut revised_split: Vec<String> =
579 rev_result.split('\n').map(|s| s.to_string()).collect();
580
581 while original_split.len() > orig.len()
582 && original_split.last().is_some_and(|s| s.is_empty())
583 {
584 original_split.pop();
585 }
586 while revised_split.len() > rev.len() && revised_split.last().is_some_and(|s| s.is_empty())
587 {
588 revised_split.pop();
589 }
590
591 let max_lines = original_split.len().max(revised_split.len());
592 let mut diff_rows = Vec::new();
593
594 for j in 0..max_lines {
595 let orig_line = original_split.get(j).map(|s| s.as_str()).unwrap_or("");
596 let rev_line = revised_split.get(j).map(|s| s.as_str()).unwrap_or("");
597 diff_rows.push(self.build_diff_row_without_normalizing(
598 Tag::Change,
599 orig_line,
600 rev_line,
601 ));
602 }
603
604 diff_rows
605 }
606
607 fn preprocess_line(&self, line: &str) -> String {
608 let normalized = (self.line_normalizer)(line);
609 if self.column_width == 0 {
610 normalized
611 } else {
612 string_utils::wrap_text(&normalized, self.column_width)
613 }
614 }
615
616 fn process_equalities(&self, text: &str) -> String {
617 if let Some(ref proc) = self.equality_processor {
618 proc(text)
619 } else {
620 text.to_string()
621 }
622 }
623}
624
625pub struct Builder {
627 show_inline_diffs: bool,
628 ignore_white_spaces: bool,
629 decompress_deltas: bool,
630 old_tag: TagGeneratorFn,
631 new_tag: TagGeneratorFn,
632 column_width: usize,
633 merge_original_revised: bool,
634 report_lines_unchanged: bool,
635 inline_diff_splitter: SplitterFn,
636 line_normalizer: StringTransformFn,
637 process_diffs: Option<StringTransformFn>,
638 equalizer: Option<EqualizerFn>,
639 replace_original_linefeed_in_changes_with_spaces: bool,
640 inline_delta_merger: InlineDeltaMergerFn,
641 equality_processor: Option<StringTransformFn>,
642}
643
644impl Builder {
645 pub fn new() -> Self {
647 Self {
648 show_inline_diffs: false,
649 ignore_white_spaces: false,
650 decompress_deltas: true,
651 old_tag: Arc::new(|_tag, f| {
652 if f {
653 "<span class=\"editOldInline\">".to_string()
654 } else {
655 "</span>".to_string()
656 }
657 }),
658 new_tag: Arc::new(|_tag, f| {
659 if f {
660 "<span class=\"editNewInline\">".to_string()
661 } else {
662 "</span>".to_string()
663 }
664 }),
665 column_width: 0,
666 merge_original_revised: false,
667 report_lines_unchanged: false,
668 inline_diff_splitter: splitter_by_character(),
669 line_normalizer: html_line_normalizer(),
670 process_diffs: None,
671 equalizer: None,
672 replace_original_linefeed_in_changes_with_spaces: false,
673 inline_delta_merger: default_inline_delta_merger(),
674 equality_processor: None,
675 }
676 }
677
678 pub fn show_inline_diffs(mut self, val: bool) -> Self {
679 self.show_inline_diffs = val;
680 self
681 }
682
683 pub fn ignore_white_spaces(mut self, val: bool) -> Self {
684 self.ignore_white_spaces = val;
685 self
686 }
687
688 pub fn report_lines_unchanged(mut self, val: bool) -> Self {
689 self.report_lines_unchanged = val;
690 self
691 }
692
693 pub fn old_tag<F>(mut self, generator: F) -> Self
694 where
695 F: Fn(Tag, bool) -> String + Send + Sync + 'static,
696 {
697 self.old_tag = Arc::new(generator);
698 self
699 }
700
701 pub fn old_tag_simple<F>(mut self, generator: F) -> Self
702 where
703 F: Fn(bool) -> String + Send + Sync + 'static,
704 {
705 self.old_tag = Arc::new(move |_tag, f| generator(f));
706 self
707 }
708
709 pub fn new_tag<F>(mut self, generator: F) -> Self
710 where
711 F: Fn(Tag, bool) -> String + Send + Sync + 'static,
712 {
713 self.new_tag = Arc::new(generator);
714 self
715 }
716
717 pub fn new_tag_simple<F>(mut self, generator: F) -> Self
718 where
719 F: Fn(bool) -> String + Send + Sync + 'static,
720 {
721 self.new_tag = Arc::new(move |_tag, f| generator(f));
722 self
723 }
724
725 pub fn process_diffs<F>(mut self, processor: F) -> Self
726 where
727 F: Fn(&str) -> String + Send + Sync + 'static,
728 {
729 self.process_diffs = Some(Arc::new(processor));
730 self
731 }
732
733 pub fn process_equalities<F>(mut self, processor: F) -> Self
734 where
735 F: Fn(&str) -> String + Send + Sync + 'static,
736 {
737 self.equality_processor = Some(Arc::new(processor));
738 self
739 }
740
741 pub fn column_width(mut self, width: usize) -> Self {
742 self.column_width = width;
743 self
744 }
745
746 pub fn merge_original_revised(mut self, merge: bool) -> Self {
747 self.merge_original_revised = merge;
748 self
749 }
750
751 pub fn decompress_deltas(mut self, decompress: bool) -> Self {
752 self.decompress_deltas = decompress;
753 self
754 }
755
756 pub fn inline_diff_by_word(mut self, inline_diff_by_word: bool) -> Self {
757 self.inline_diff_splitter = if inline_diff_by_word {
758 splitter_by_word()
759 } else {
760 splitter_by_character()
761 };
762 self
763 }
764
765 pub fn inline_diff_by_splitter<F>(mut self, splitter: F) -> Self
766 where
767 F: Fn(&str) -> Vec<String> + Send + Sync + 'static,
768 {
769 self.inline_diff_splitter = Arc::new(splitter);
770 self
771 }
772
773 pub fn line_normalizer<F>(mut self, normalizer: F) -> Self
774 where
775 F: Fn(&str) -> String + Send + Sync + 'static,
776 {
777 self.line_normalizer = Arc::new(normalizer);
778 self
779 }
780
781 pub fn equalizer<F>(mut self, equalizer: F) -> Self
782 where
783 F: Fn(&str, &str) -> bool + Send + Sync + 'static,
784 {
785 self.equalizer = Some(Arc::new(equalizer));
786 self
787 }
788
789 pub fn replace_original_linefeed_in_changes_with_spaces(mut self, replace: bool) -> Self {
790 self.replace_original_linefeed_in_changes_with_spaces = replace;
791 self
792 }
793
794 pub fn inline_delta_merger<F>(mut self, merger: F) -> Self
795 where
796 F: Fn(&InlineDeltaMergeInfo<String>) -> Vec<Delta<String>> + Send + Sync + 'static,
797 {
798 self.inline_delta_merger = Arc::new(merger);
799 self
800 }
801
802 pub fn inline_delta_merger_arc(mut self, merger: InlineDeltaMergerFn) -> Self {
803 self.inline_delta_merger = merger;
804 self
805 }
806
807 pub fn build(self) -> DiffRowGenerator {
808 let equalizer = match self.equalizer {
809 Some(eq) => eq,
810 None => {
811 if self.ignore_white_spaces {
812 ignore_whitespace_equalizer()
813 } else {
814 default_equalizer()
815 }
816 }
817 };
818
819 DiffRowGenerator {
820 column_width: self.column_width,
821 equalizer,
822 ignore_white_spaces: self.ignore_white_spaces,
823 inline_diff_splitter: self.inline_diff_splitter,
824 merge_original_revised: self.merge_original_revised,
825 old_tag: self.old_tag,
826 new_tag: self.new_tag,
827 report_lines_unchanged: self.report_lines_unchanged,
828 line_normalizer: self.line_normalizer,
829 process_diffs: self.process_diffs,
830 inline_delta_merger: self.inline_delta_merger,
831 equality_processor: self.equality_processor,
832 show_inline_diffs: self.show_inline_diffs,
833 replace_original_linefeed_in_changes_with_spaces: self
834 .replace_original_linefeed_in_changes_with_spaces,
835 decompress_deltas: self.decompress_deltas,
836 }
837 }
838}
839
840impl Default for Builder {
841 fn default() -> Self {
842 Self::new()
843 }
844}