Add context diff and tests for it
This commit is contained in:
+63
-1
@@ -3,8 +3,9 @@ pub mod differ;
|
|||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
use sequencematcher::{SequenceMatcher, Sequence};
|
use sequencematcher::{SequenceMatcher, Sequence};
|
||||||
use utils::{format_range_unified};
|
use utils::{format_range_unified, format_range_context};
|
||||||
|
|
||||||
|
|
||||||
pub fn get_close_matches<'a>(word: &str, possibilities: Vec<&'a str>, n: usize, cutoff: f32) -> Vec<&'a str> {
|
pub fn get_close_matches<'a>(word: &str, possibilities: Vec<&'a str>, n: usize, cutoff: f32) -> Vec<&'a str> {
|
||||||
@@ -63,3 +64,64 @@ pub fn unified_diff<T: Sequence>(first_sequence: &T, second_sequence: &T, from_f
|
|||||||
}
|
}
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn context_diff<T: Sequence>(first_sequence: &T, second_sequence: &T, from_file: &str, to_file: &str,
|
||||||
|
from_file_date: &str, to_file_date: &str, n: usize, lineterm: &str) -> Vec<String> {
|
||||||
|
let mut res = Vec::new();
|
||||||
|
let mut prefix: HashMap<String, String> = HashMap::new();
|
||||||
|
prefix.insert(String::from("insert"), String::from("+ "));
|
||||||
|
prefix.insert(String::from("delete"), String::from("- "));
|
||||||
|
prefix.insert(String::from("replace"), String::from("! "));
|
||||||
|
prefix.insert(String::from("equal"), String::from(" "));
|
||||||
|
let mut started = false;
|
||||||
|
let mut matcher = SequenceMatcher::new(first_sequence, second_sequence);
|
||||||
|
for group in &matcher.get_grouped_opcodes(n) {
|
||||||
|
if !started {
|
||||||
|
started = true;
|
||||||
|
let from_date = format!("\t{}", from_file_date);
|
||||||
|
let to_date = format!("\t{}", to_file_date);
|
||||||
|
res.push( format!("*** {}{}{}", from_file, from_date, lineterm) );
|
||||||
|
res.push( format!("--- {}{}{}", to_file, to_date, lineterm) );
|
||||||
|
}
|
||||||
|
let (first, last) = (group.first().unwrap(), group.last().unwrap());
|
||||||
|
res.push(format!("***************{}", lineterm));
|
||||||
|
let file1_range = format_range_context(first.first_start, last.first_end);
|
||||||
|
res.push(format!("*** {} ****{}", file1_range, lineterm));
|
||||||
|
let mut any = false;
|
||||||
|
for opcode in group {
|
||||||
|
if opcode.tag == "replace" || opcode.tag == "delete" {
|
||||||
|
any = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if any {
|
||||||
|
for opcode in group {
|
||||||
|
if opcode.tag != "insert" {
|
||||||
|
for i in opcode.first_start..opcode.first_end {
|
||||||
|
res.push( format!("{}{}", prefix.get(&opcode.tag).unwrap(), first_sequence.at_index(i).unwrap()) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let file2_range = format_range_context(first.second_start, last.second_end);
|
||||||
|
res.push(format!("--- {} ----{}", file1_range, lineterm));
|
||||||
|
any = false;
|
||||||
|
for opcode in group {
|
||||||
|
if opcode.tag == "replace" || opcode.tag == "insert" {
|
||||||
|
any = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if any {
|
||||||
|
for opcode in group {
|
||||||
|
if opcode.tag != "delete" {
|
||||||
|
for i in opcode.second_start..opcode.second_end {
|
||||||
|
res.push( format!("{}{}", prefix.get(&opcode.tag).unwrap(), second_sequence.at_index(i).unwrap()) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
+14
-1
@@ -30,10 +30,23 @@ pub fn format_range_unified(start:usize, end: usize) -> String {
|
|||||||
println!("{:?} {}", start, end);
|
println!("{:?} {}", start, end);
|
||||||
let length = end - start;
|
let length = end - start;
|
||||||
if length == 1 {
|
if length == 1 {
|
||||||
return format!("{}", beginning)
|
return beginning.to_string()
|
||||||
}
|
}
|
||||||
if length == 0 {
|
if length == 0 {
|
||||||
beginning -= 1;
|
beginning -= 1;
|
||||||
}
|
}
|
||||||
format!("{},{}", beginning, length)
|
format!("{},{}", beginning, length)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn format_range_context(start: usize, end: usize) -> String {
|
||||||
|
let mut beginning = start + 1;
|
||||||
|
let length = end - start;
|
||||||
|
if length == 0 {
|
||||||
|
beginning -= 1
|
||||||
|
}
|
||||||
|
if length <= 1 {
|
||||||
|
return beginning.to_string()
|
||||||
|
}
|
||||||
|
format!("{},{}", beginning, beginning + length - 1)
|
||||||
}
|
}
|
||||||
@@ -109,5 +109,13 @@ fn test_unified_diff() {
|
|||||||
let result = difflib::unified_diff(&first_text, &second_text, "Original", "Current",
|
let result = difflib::unified_diff(&first_text, &second_text, "Original", "Current",
|
||||||
"2005-01-26 23:30:50", "2010-04-02 10:20:52", 3, "").join("");
|
"2005-01-26 23:30:50", "2010-04-02 10:20:52", 3, "").join("");
|
||||||
assert_eq!(result, "--- Original\t2005-01-26 23:30:50+++ Current\t2010-04-02 10:20:52@@ -1,4 +1,4 @@+zero one-two-three+tree four");
|
assert_eq!(result, "--- Original\t2005-01-26 23:30:50+++ Current\t2010-04-02 10:20:52@@ -1,4 +1,4 @@+zero one-two-three+tree four");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_context_diff() {
|
||||||
|
let first_text = "one two three four".split(" ").collect::<Vec<&str>>();
|
||||||
|
let second_text = "zero one tree four".split(" ").collect::<Vec<&str>>();
|
||||||
|
let result = difflib::context_diff(&first_text, &second_text, "Original", "Current",
|
||||||
|
"2005-01-26 23:30:50", "2010-04-02 10:20:52", 3, "").join("");
|
||||||
|
assert_eq!(result, "*** Original\t2005-01-26 23:30:50--- Current\t2010-04-02 10:20:52****************** 1,4 **** one! two! three four--- 1,4 ----+ zero one! tree four");
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user