Change spaces to tab

This commit is contained in:
Dima Kudosh
2016-02-29 14:05:31 +03:00
parent 37b2c3697e
commit 0035a1b816
5 changed files with 448 additions and 448 deletions
+37 -37
View File
@@ -5,46 +5,46 @@ use difflib::sequencematcher::SequenceMatcher;
fn main() {
// unified_diff
let first_text = "one two three four".split(" ").collect::<Vec<&str>>();
let second_text = "zero one tree four".split(" ").collect::<Vec<&str>>();
let diff = difflib::unified_diff(&first_text, &second_text, "Original", "Current",
"2005-01-26 23:30:50", "2010-04-02 10:20:52", 3, "");
for line in &diff {
println!("{:?}", line);
}
// unified_diff
let first_text = "one two three four".split(" ").collect::<Vec<&str>>();
let second_text = "zero one tree four".split(" ").collect::<Vec<&str>>();
let diff = difflib::unified_diff(&first_text, &second_text, "Original", "Current",
"2005-01-26 23:30:50", "2010-04-02 10:20:52", 3, "");
for line in &diff {
println!("{:?}", line);
}
//context_diff
let diff = difflib::context_diff(&first_text, &second_text, "Original", "Current",
"2005-01-26 23:30:50", "2010-04-02 10:20:52", 3, "");
for line in &diff {
println!("{:?}", line);
}
//context_diff
let diff = difflib::context_diff(&first_text, &second_text, "Original", "Current",
"2005-01-26 23:30:50", "2010-04-02 10:20:52", 3, "");
for line in &diff {
println!("{:?}", line);
}
//get_close_matches
let words = vec!["ape", "apple", "peach", "puppy"];
let result = difflib::get_close_matches("appel", words, 3, 0.6);
println!("{:?}", result);
let words = vec!["ape", "apple", "peach", "puppy"];
let result = difflib::get_close_matches("appel", words, 3, 0.6);
println!("{:?}", result);
//Differ examples
let differ = Differ::new();
let diff = differ.compare(&first_text, &second_text);
for line in &diff {
println!("{:?}", line);
}
//Differ examples
let differ = Differ::new();
let diff = differ.compare(&first_text, &second_text);
for line in &diff {
println!("{:?}", line);
}
//SequenceMatcher examples
let mut matcher = SequenceMatcher::new("one two three four", "zero one tree four");
let m = matcher.find_longest_match(0, 18, 0, 18);
println!("{:?}", m);
let all_matches = matcher.get_matching_blocks();
println!("{:?}", all_matches);
let opcode = matcher.get_opcodes();
println!("{:?}", opcode);
let grouped_opcodes = matcher.get_grouped_opcodes(2);
println!("{:?}", grouped_opcodes);
let ratio = matcher.ratio();
println!("{:?}", ratio);
matcher.set_seqs("aaaaa", "aaaab");
println!("{:?}", matcher.ratio());
//SequenceMatcher examples
let mut matcher = SequenceMatcher::new("one two three four", "zero one tree four");
let m = matcher.find_longest_match(0, 18, 0, 18);
println!("{:?}", m);
let all_matches = matcher.get_matching_blocks();
println!("{:?}", all_matches);
let opcode = matcher.get_opcodes();
println!("{:?}", opcode);
let grouped_opcodes = matcher.get_grouped_opcodes(2);
println!("{:?}", grouped_opcodes);
let ratio = matcher.ratio();
println!("{:?}", ratio);
matcher.set_seqs("aaaaa", "aaaab");
println!("{:?}", matcher.ratio());
}
+174 -174
View File
@@ -18,204 +18,204 @@ impl Differ{
pub fn compare<T: ?Sized + Sequence>(&self, first_sequence: &T, second_sequence: &T) -> Vec<String> {
let mut matcher = SequenceMatcher::new(first_sequence, second_sequence);
matcher.set_is_junk(self.line_junk);
matcher.set_is_junk(self.line_junk);
let mut res = Vec::new();
for opcode in matcher.get_opcodes(){
let mut gen = Vec::new();
let mut gen = Vec::new();
match opcode.tag.as_ref() {
"replace" => { gen = self.fancy_replace(first_sequence, opcode.first_start, opcode.first_end,
second_sequence, opcode.second_start, opcode.second_end) },
"delete" => { gen = self.dump("-", first_sequence, opcode.first_start, opcode.first_end) },
"insert" => { gen = self.dump("+", second_sequence, opcode.second_start, opcode.second_end) },
"equal" => { gen = self.dump(" ", first_sequence, opcode.first_start, opcode.first_end) },
_ => {}
"replace" => { gen = self.fancy_replace(first_sequence, opcode.first_start, opcode.first_end,
second_sequence, opcode.second_start, opcode.second_end) },
"delete" => { gen = self.dump("-", first_sequence, opcode.first_start, opcode.first_end) },
"insert" => { gen = self.dump("+", second_sequence, opcode.second_start, opcode.second_end) },
"equal" => { gen = self.dump(" ", first_sequence, opcode.first_start, opcode.first_end) },
_ => {}
}
for i in gen {
res.push(i);
}
for i in gen {
res.push(i);
}
}
res
}
fn dump<T: ?Sized + Sequence>(&self, tag: &str, sequence: &T, start: usize, end: usize) -> Vec<String> {
let mut res = Vec::new();
for i in start..end {
match sequence.at_index(i) {
Some(s) => res.push(format!("{} {}", tag, s)),
None => {},
}
}
for i in start..end {
match sequence.at_index(i) {
Some(s) => res.push(format!("{} {}", tag, s)),
None => {},
}
}
res
}
fn plain_replace<T: ?Sized + Sequence>(&self, first_sequence: &T, first_start: usize, first_end: usize,
second_sequence: &T, second_start: usize, second_end: usize) -> Vec<String> {
if !(first_start < first_end && second_start < second_end){
return Vec::new();
}
let mut first = Vec::new();
let mut second = Vec::new();
if second_end - second_start < first_end - first_start{
first = self.dump("+", second_sequence, second_start, second_end);
second = self.dump("-", first_sequence, first_start, first_end);
} else {
first = self.dump("-", first_sequence, first_start, first_end);
second = self.dump("+", second_sequence, second_start, second_end);
}
for s in second{
first.push(s);
}
first
}
fn plain_replace<T: ?Sized + Sequence>(&self, first_sequence: &T, first_start: usize, first_end: usize,
second_sequence: &T, second_start: usize, second_end: usize) -> Vec<String> {
if !(first_start < first_end && second_start < second_end){
return Vec::new();
}
let mut first = Vec::new();
let mut second = Vec::new();
if second_end - second_start < first_end - first_start{
first = self.dump("+", second_sequence, second_start, second_end);
second = self.dump("-", first_sequence, first_start, first_end);
} else {
first = self.dump("-", first_sequence, first_start, first_end);
second = self.dump("+", second_sequence, second_start, second_end);
}
for s in second{
first.push(s);
}
first
}
fn fancy_replace<T: ?Sized + Sequence>(&self, first_sequence: &T, first_start: usize, first_end: usize,
second_sequence: &T, second_start: usize, second_end: usize) -> Vec<String> {
let mut res = Vec::new();
let (mut best_ratio, cutoff) = (0.74, 0.75);
let (mut best_i, mut best_j) = (0, 0);
let (mut second_sequence_str, mut first_sequence_str) = ("", "");
let mut eqi: Option<usize> = None;
let mut eqj: Option<usize> = None;
for j in second_start..second_end{
second_sequence_str = second_sequence.at_index(j).unwrap();
for i in first_start..first_end{
first_sequence_str = first_sequence.at_index(i).unwrap();
if first_sequence_str == second_sequence_str{
if eqi.is_none(){
eqi = Some(i);
eqj = Some(j);
}
continue;
}
let mut cruncher = SequenceMatcher::new(first_sequence_str, second_sequence_str);
cruncher.set_is_junk(self.char_junk);
if cruncher.ratio() > best_ratio{
best_ratio = cruncher.ratio();
best_i = i;
best_j = j;
}
}
}
if best_ratio < cutoff{
if eqi.is_none(){
res.extend(self.plain_replace(first_sequence, first_start, first_end, second_sequence, second_start, second_end).iter().cloned());
return res
}
best_i = eqi.unwrap();
best_j = eqj.unwrap();
best_ratio = 1.0;
} else {
eqi = None;
}
res.extend(self.fancy_helper(first_sequence, first_start, best_i, second_sequence, second_start, best_j).iter().cloned());
let (first_element, second_element) = (first_sequence.at_index(best_i).unwrap(), second_sequence.at_index(best_j).unwrap());
if eqi.is_none(){
let (mut first_tag, mut second_tag) = (String::new(), String::new());
let mut cruncher = SequenceMatcher::new(first_element, second_element);
cruncher.set_is_junk(self.char_junk);
for opcode in &cruncher.get_opcodes(){
let (first_length, second_length) = (opcode.first_end - opcode.first_start, opcode.second_end - opcode.second_start);
match opcode.tag.as_ref() {
"replace" => {
first_tag.push_str(&str_with_similar_chars('^', first_length));
second_tag.push_str(&str_with_similar_chars('^', second_length));
},
"delete" => {
first_tag.push_str(&str_with_similar_chars('-', first_length));
},
"insert" => {
second_tag.push_str(&str_with_similar_chars('+', second_length));
},
"equal" => {
first_tag.push_str(&str_with_similar_chars(' ', first_length));
second_tag.push_str(&str_with_similar_chars(' ', second_length));
},
_ => {}
}
}
res.extend(self.qformat(&first_element, &second_element, &first_tag, &second_tag).iter().cloned());
} else {
let mut s = String::from(" ");
s.push_str(&first_element);
res.push(s);
}
res.extend(self.fancy_helper(first_sequence, best_i + 1, first_end, second_sequence, best_j + 1, second_end).iter().cloned());
res
}
fn fancy_replace<T: ?Sized + Sequence>(&self, first_sequence: &T, first_start: usize, first_end: usize,
second_sequence: &T, second_start: usize, second_end: usize) -> Vec<String> {
let mut res = Vec::new();
let (mut best_ratio, cutoff) = (0.74, 0.75);
let (mut best_i, mut best_j) = (0, 0);
let (mut second_sequence_str, mut first_sequence_str) = ("", "");
let mut eqi: Option<usize> = None;
let mut eqj: Option<usize> = None;
for j in second_start..second_end{
second_sequence_str = second_sequence.at_index(j).unwrap();
for i in first_start..first_end{
first_sequence_str = first_sequence.at_index(i).unwrap();
if first_sequence_str == second_sequence_str{
if eqi.is_none(){
eqi = Some(i);
eqj = Some(j);
}
continue;
}
let mut cruncher = SequenceMatcher::new(first_sequence_str, second_sequence_str);
cruncher.set_is_junk(self.char_junk);
if cruncher.ratio() > best_ratio{
best_ratio = cruncher.ratio();
best_i = i;
best_j = j;
}
}
}
if best_ratio < cutoff{
if eqi.is_none(){
res.extend(self.plain_replace(first_sequence, first_start, first_end, second_sequence, second_start, second_end).iter().cloned());
return res
}
best_i = eqi.unwrap();
best_j = eqj.unwrap();
best_ratio = 1.0;
} else {
eqi = None;
}
res.extend(self.fancy_helper(first_sequence, first_start, best_i, second_sequence, second_start, best_j).iter().cloned());
let (first_element, second_element) = (first_sequence.at_index(best_i).unwrap(), second_sequence.at_index(best_j).unwrap());
if eqi.is_none(){
let (mut first_tag, mut second_tag) = (String::new(), String::new());
let mut cruncher = SequenceMatcher::new(first_element, second_element);
cruncher.set_is_junk(self.char_junk);
for opcode in &cruncher.get_opcodes(){
let (first_length, second_length) = (opcode.first_end - opcode.first_start, opcode.second_end - opcode.second_start);
match opcode.tag.as_ref() {
"replace" => {
first_tag.push_str(&str_with_similar_chars('^', first_length));
second_tag.push_str(&str_with_similar_chars('^', second_length));
},
"delete" => {
first_tag.push_str(&str_with_similar_chars('-', first_length));
},
"insert" => {
second_tag.push_str(&str_with_similar_chars('+', second_length));
},
"equal" => {
first_tag.push_str(&str_with_similar_chars(' ', first_length));
second_tag.push_str(&str_with_similar_chars(' ', second_length));
},
_ => {}
}
}
res.extend(self.qformat(&first_element, &second_element, &first_tag, &second_tag).iter().cloned());
} else {
let mut s = String::from(" ");
s.push_str(&first_element);
res.push(s);
}
res.extend(self.fancy_helper(first_sequence, best_i + 1, first_end, second_sequence, best_j + 1, second_end).iter().cloned());
res
}
fn fancy_helper<T: ?Sized + Sequence>(&self, first_sequence: &T, first_start: usize, first_end: usize,
second_sequence: &T, second_start: usize, second_end: usize) -> Vec<String> {
let mut res = Vec::new();
if first_start < first_end{
if second_start < second_end{
res = self.fancy_replace(first_sequence, first_start, first_end, second_sequence, second_start, second_end);
} else {
res = self.dump("-", first_sequence, first_start, first_end);
}
} else if second_start < second_end {
res = self.dump("+", second_sequence, second_start, second_end);
}
res
}
fn fancy_helper<T: ?Sized + Sequence>(&self, first_sequence: &T, first_start: usize, first_end: usize,
second_sequence: &T, second_start: usize, second_end: usize) -> Vec<String> {
let mut res = Vec::new();
if first_start < first_end{
if second_start < second_end{
res = self.fancy_replace(first_sequence, first_start, first_end, second_sequence, second_start, second_end);
} else {
res = self.dump("-", first_sequence, first_start, first_end);
}
} else if second_start < second_end {
res = self.dump("+", second_sequence, second_start, second_end);
}
res
}
fn qformat(&self, first_line: &str, second_line: &str, first_tags: &str, second_tags: &str) -> Vec<String> {
let mut res = Vec::new();
let mut first_tags = first_tags;
let mut second_tags = second_tags;
let mut common = cmp::min(count_leading(first_line, '\t'), count_leading(second_line, '\t'));
common = cmp::min(common, count_leading(first_tags.split_at(common).0, ' '));
common = cmp::min(common, count_leading(first_tags.split_at(common).0, ' '));
first_tags = first_tags.split_at(common).1.trim_right();
second_tags = second_tags.split_at(common).1.trim_right();
let mut s = String::from(format!("- {}", first_line));
res.push(s);
if first_tags != ""{
s = String::from(format!("? {}{}\n", str_with_similar_chars('\t', common), first_tags));
res.push(s);
}
s = String::from(format!("+ {}", second_line));
res.push(s);
if second_tags != "" {
s = String::from(format!("? {}{}\n", str_with_similar_chars('\t', common), second_tags));
res.push(s);
}
res
}
fn qformat(&self, first_line: &str, second_line: &str, first_tags: &str, second_tags: &str) -> Vec<String> {
let mut res = Vec::new();
let mut first_tags = first_tags;
let mut second_tags = second_tags;
let mut common = cmp::min(count_leading(first_line, '\t'), count_leading(second_line, '\t'));
common = cmp::min(common, count_leading(first_tags.split_at(common).0, ' '));
common = cmp::min(common, count_leading(first_tags.split_at(common).0, ' '));
first_tags = first_tags.split_at(common).1.trim_right();
second_tags = second_tags.split_at(common).1.trim_right();
let mut s = String::from(format!("- {}", first_line));
res.push(s);
if first_tags != ""{
s = String::from(format!("? {}{}\n", str_with_similar_chars('\t', common), first_tags));
res.push(s);
}
s = String::from(format!("+ {}", second_line));
res.push(s);
if second_tags != "" {
s = String::from(format!("? {}{}\n", str_with_similar_chars('\t', common), second_tags));
res.push(s);
}
res
}
pub fn restore(delta: &Vec<String>, which: usize) -> Vec<String> {
if !(which == 1 || which == 2) {
panic!("Second parameter must be 1 or 2");
}
let mut res = Vec::new();
let mut tag = String::new();
if which == 1 {
tag = "- ".to_string();
} else {
tag = "+ ".to_string();
}
let prefixes = vec![tag, " ".to_string()];
for line in delta {
for prefix in &prefixes {
if line.starts_with(prefix) {
res.push( line.split_at(2).1.to_string() );
}
}
}
res
}
pub fn restore(delta: &Vec<String>, which: usize) -> Vec<String> {
if !(which == 1 || which == 2) {
panic!("Second parameter must be 1 or 2");
}
let mut res = Vec::new();
let mut tag = String::new();
if which == 1 {
tag = "- ".to_string();
} else {
tag = "+ ".to_string();
}
let prefixes = vec![tag, " ".to_string()];
for line in delta {
for prefix in &prefixes {
if line.starts_with(prefix) {
res.push( line.split_at(2).1.to_string() );
}
}
}
res
}
}
#[test]
fn test_fancy_replace() {
let differ = Differ::new();
let result = differ.fancy_replace(&vec!["abcDefghiJkl\n"], 0, 1, &vec!["abcdefGhijkl\n"], 0, 1).join("");
assert_eq!(result, "- abcDefghiJkl\n? ^ ^ ^\n+ abcdefGhijkl\n? ^ ^ ^\n");
let differ = Differ::new();
let result = differ.fancy_replace(&vec!["abcDefghiJkl\n"], 0, 1, &vec!["abcdefGhijkl\n"], 0, 1).join("");
assert_eq!(result, "- abcDefghiJkl\n? ^ ^ ^\n+ abcdefGhijkl\n? ^ ^ ^\n");
}
#[test]
fn test_qformat() {
let differ = Differ::new();
let result = differ.qformat("\tabcDefghiJkl\n", "\tabcdefGhijkl\n", " ^ ^ ^ ", " ^ ^ ^ ");
assert_eq!(result, vec!["- \tabcDefghiJkl\n", "? \t ^ ^ ^\n", "+ \tabcdefGhijkl\n", "? \t ^ ^ ^\n"]);
let differ = Differ::new();
let result = differ.qformat("\tabcDefghiJkl\n", "\tabcdefGhijkl\n", " ^ ^ ^ ", " ^ ^ ^ ");
assert_eq!(result, vec!["- \tabcDefghiJkl\n", "? \t ^ ^ ^\n", "+ \tabcdefGhijkl\n", "? \t ^ ^ ^\n"]);
}
+112 -112
View File
@@ -42,49 +42,49 @@ impl Opcode{
}
pub trait Sequence: Debug {
fn len(&self) -> usize;
fn at_index(&self, index: usize) -> Option<&str>;
fn len(&self) -> usize;
fn at_index(&self, index: usize) -> Option<&str>;
}
impl Sequence for str {
fn len(&self) -> usize {
self.len()
}
fn len(&self) -> usize {
self.len()
}
fn at_index(&self, index: usize) -> Option<&str> {
if index > self.len(){
return None
}
unsafe{
Some(self.slice_unchecked(index, index + 1))
}
}
fn at_index(&self, index: usize) -> Option<&str> {
if index > self.len(){
return None
}
unsafe{
Some(self.slice_unchecked(index, index + 1))
}
}
}
impl<'a> Sequence for Vec<&'a str> {
fn len(&self) -> usize {
self.len()
}
fn len(&self) -> usize {
self.len()
}
fn at_index(&self, index: usize) -> Option<&str> {
if index < self.len() && index >= 0 {
return Some(self[index])
}
None
}
fn at_index(&self, index: usize) -> Option<&str> {
if index < self.len() && index >= 0 {
return Some(self[index])
}
None
}
}
impl<'a> Sequence for [&'a str] {
fn len(&self) -> usize {
self.len()
}
self.len()
}
fn at_index(&self, index: usize) -> Option<&str> {
if index < self.len() && index >= 0 {
return Some(self[index])
}
None
}
fn at_index(&self, index: usize) -> Option<&str> {
if index < self.len() && index >= 0 {
return Some(self[index])
}
None
}
}
pub struct SequenceMatcher<'a, T: 'a + ?Sized + Sequence>{
@@ -134,43 +134,43 @@ impl<'a, T: ?Sized + Sequence> SequenceMatcher<'a, T>{
self.second_sequence = sequence;
self.matching_blocks = None;
self.opcodes = None;
self.chain_second_seq();
self.chain_second_seq();
}
fn chain_second_seq(&mut self) {
let second_sequence = self.second_sequence;
let mut second_sequence_elements = HashMap::new();
for i in 0..second_sequence.len() {
let mut counter = second_sequence_elements.entry(second_sequence.at_index(i).unwrap()).or_insert(Vec::new());
counter.push(i);
}
if self.is_junk.is_some() {
let mut junk = Vec::new();
for element in second_sequence_elements.keys() {
if (self.is_junk.unwrap())(element) {
junk.push(element.clone());
}
}
for element in &junk {
second_sequence_elements.remove(element);
}
}
let mut popular = Vec::new();
let len = second_sequence.len();
if len >= 200 {
let test_len = (len as f32 / 100.0).floor() as usize + 1;
for (element, indexes) in second_sequence_elements.iter() {
if indexes.len() > test_len {
popular.push(element.clone());
}
}
for element in &popular {
second_sequence_elements.remove(element);
}
}
self.second_sequence_elements = second_sequence_elements;
self.second_sequence_popular = popular;
}
fn chain_second_seq(&mut self) {
let second_sequence = self.second_sequence;
let mut second_sequence_elements = HashMap::new();
for i in 0..second_sequence.len() {
let mut counter = second_sequence_elements.entry(second_sequence.at_index(i).unwrap()).or_insert(Vec::new());
counter.push(i);
}
if self.is_junk.is_some() {
let mut junk = Vec::new();
for element in second_sequence_elements.keys() {
if (self.is_junk.unwrap())(element) {
junk.push(element.clone());
}
}
for element in &junk {
second_sequence_elements.remove(element);
}
}
let mut popular = Vec::new();
let len = second_sequence.len();
if len >= 200 {
let test_len = (len as f32 / 100.0).floor() as usize + 1;
for (element, indexes) in second_sequence_elements.iter() {
if indexes.len() > test_len {
popular.push(element.clone());
}
}
for element in &popular {
second_sequence_elements.remove(element);
}
}
self.second_sequence_elements = second_sequence_elements;
self.second_sequence_popular = popular;
}
pub fn find_longest_match(&self, first_start: usize, first_end: usize, second_start: usize, second_end: usize) -> Match {
let first_sequence = &self.first_sequence;
@@ -181,34 +181,34 @@ impl<'a, T: ?Sized + Sequence> SequenceMatcher<'a, T>{
for i in first_start..first_end {
let mut new_j2len: HashMap<usize, usize> = HashMap::new();
match second_sequence_elements.get(first_sequence.at_index(i).unwrap()) {
Some(indexes) => {
for j in indexes {
let j = j.clone();
if j < second_start {
continue;
};
if j >= second_end {
break;
};
let mut size = 0;
if j > 0 {
match j2len.get(&(j-1)){
Some(k) => {
size = k.clone();
},
None => {}
}
}
size += 1;
new_j2len.insert(j, size);
if size > best_size {
best_i = i + 1 - size;
best_j = j + 1 - size;
best_size = size;
}
}
},
None => {},
Some(indexes) => {
for j in indexes {
let j = j.clone();
if j < second_start {
continue;
};
if j >= second_end {
break;
};
let mut size = 0;
if j > 0 {
match j2len.get(&(j-1)){
Some(k) => {
size = k.clone();
},
None => {}
}
}
size += 1;
new_j2len.insert(j, size);
if size > best_size {
best_i = i + 1 - size;
best_j = j + 1 - size;
best_size = size;
}
}
},
None => {},
}
j2len = new_j2len;
}
@@ -223,13 +223,13 @@ impl<'a, T: ?Sized + Sequence> SequenceMatcher<'a, T>{
first_sequence.at_index(best_i + best_size) == second_sequence.at_index(best_j + best_size) {
best_size += 1;
}
}
Match::new(best_i, best_j, best_size)
}
Match::new(best_i, best_j, best_size)
}
pub fn get_matching_blocks(&mut self) -> Vec<Match> {
if self.matching_blocks.as_ref().is_some(){
return self.matching_blocks.as_ref().unwrap().clone()
return self.matching_blocks.as_ref().unwrap().clone()
}
let (first_length, second_length) = (self.first_sequence.len(), self.second_sequence.len());
let mut matches = Vec::new();
@@ -239,15 +239,15 @@ impl<'a, T: ?Sized + Sequence> SequenceMatcher<'a, T>{
let m = self.find_longest_match(first_start, first_end, second_start, second_end);
match m.size {
0 => {},
_ => {
if first_start < m.first_start && second_start < m.second_start{
queue.push((first_start, m.first_start, second_start, m.second_start));
}
if m.first_start + m.size < first_end && m.second_start + m.size < second_end{
queue.push((m.first_start + m.size, first_end, m.second_start + m.size, second_end));
}
matches.push(m);
},
_ => {
if first_start < m.first_start && second_start < m.second_start{
queue.push((first_start, m.first_start, second_start, m.second_start));
}
if m.first_start + m.size < first_end && m.second_start + m.size < second_end{
queue.push((m.first_start + m.size, first_end, m.second_start + m.size, second_end));
}
matches.push(m);
},
}
}
matches.sort_by(|a, b| a.cmp(b));
@@ -258,12 +258,12 @@ impl<'a, T: ?Sized + Sequence> SequenceMatcher<'a, T>{
size += m.size
}
else {
if size != 0{
non_adjacent.push(Match::new(first_start, second_start, size));
}
first_start = m.first_start;
second_start = m.second_start;
size = m.size;
if size != 0{
non_adjacent.push(Match::new(first_start, second_start, size));
}
first_start = m.first_start;
second_start = m.second_start;
size = m.size;
}
}
if size != 0{
@@ -276,7 +276,7 @@ impl<'a, T: ?Sized + Sequence> SequenceMatcher<'a, T>{
pub fn get_opcodes(&mut self) -> Vec<Opcode>{
if self.opcodes.as_ref().is_some(){
return self.opcodes.as_ref().unwrap().clone()
return self.opcodes.as_ref().unwrap().clone()
}
let mut opcodes = Vec::new();
let (mut i, mut j) = (0, 0);
+29 -29
View File
@@ -7,45 +7,45 @@ pub fn calculate_ratio(matches: usize, length: usize) -> f32{
pub fn str_with_similar_chars(c: char, length: usize) -> String {
let mut s = String::new();
for _ in 0..length{
s.push_str(&c.to_string());
}
s
let mut s = String::new();
for _ in 0..length{
s.push_str(&c.to_string());
}
s
}
pub fn count_leading(line: &str, c: char) -> usize {
let (mut i, n) = (0, line.len());
let line: Vec<char> = line.chars().collect();
while (i < n) && line[i] == c {
i += 1;
}
return i
let (mut i, n) = (0, line.len());
let line: Vec<char> = line.chars().collect();
while (i < n) && line[i] == c {
i += 1;
}
return i
}
pub fn format_range_unified(start:usize, end: usize) -> String {
let mut beginning = start + 1;
let length = end - start;
if length == 1 {
return beginning.to_string()
}
if length == 0 {
beginning -= 1;
}
format!("{},{}", beginning, length)
let mut beginning = start + 1;
let length = end - start;
if length == 1 {
return beginning.to_string()
}
if length == 0 {
beginning -= 1;
}
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)
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)
}
+96 -96
View File
@@ -7,10 +7,10 @@ use difflib::differ::Differ;
fn test_longest_match()
{
let matcher = SequenceMatcher::new(" abcd", "abcd abcd");
let m = matcher.find_longest_match(0, 5, 0, 9);
assert_eq!(m.first_start, 0);
assert_eq!(m.second_start, 4);
assert_eq!(m.size, 5);
let m = matcher.find_longest_match(0, 5, 0, 9);
assert_eq!(m.first_start, 0);
assert_eq!(m.second_start, 4);
assert_eq!(m.size, 5);
}
#[test]
@@ -18,131 +18,131 @@ fn test_all_matches()
{
let mut matcher = SequenceMatcher::new("abxcd", "abcd");
let result = matcher.get_matching_blocks();
let mut expected_result = Vec::new();
expected_result.push(Match{
first_start: 0,
second_start: 0,
size: 2
});
expected_result.push(Match{
first_start: 3,
second_start: 2,
size: 2
});
expected_result.push(Match{
first_start: 5,
second_start: 4,
size: 0
});
assert_eq!(result, expected_result);
let mut expected_result = Vec::new();
expected_result.push(Match{
first_start: 0,
second_start: 0,
size: 2
});
expected_result.push(Match{
first_start: 3,
second_start: 2,
size: 2
});
expected_result.push(Match{
first_start: 5,
second_start: 4,
size: 0
});
assert_eq!(result, expected_result);
}
#[test]
fn test_get_opcodes() {
let mut matcher = SequenceMatcher::new("qabxcd", "abycdf");
let result = matcher.get_opcodes();
let mut expected_result = Vec::new();
expected_result.push(Opcode{
tag: "delete".to_string(),
first_start: 0,
first_end: 1,
second_start: 0,
second_end: 0
});
expected_result.push(Opcode{
tag: "equal".to_string(),
first_start: 1,
first_end: 3,
second_start: 0,
second_end: 2
});
expected_result.push(Opcode{
tag: "replace".to_string(),
first_start: 3,
first_end: 4,
second_start: 2,
second_end: 3
});
expected_result.push(Opcode{
tag: "equal".to_string(),
first_start: 4,
first_end: 6,
second_start: 3,
second_end: 5
});
expected_result.push(Opcode{
tag: "insert".to_string(),
first_start: 6,
first_end: 6,
second_start: 5,
second_end: 6
});
assert_eq!(result, expected_result);
let mut matcher = SequenceMatcher::new("qabxcd", "abycdf");
let result = matcher.get_opcodes();
let mut expected_result = Vec::new();
expected_result.push(Opcode{
tag: "delete".to_string(),
first_start: 0,
first_end: 1,
second_start: 0,
second_end: 0
});
expected_result.push(Opcode{
tag: "equal".to_string(),
first_start: 1,
first_end: 3,
second_start: 0,
second_end: 2
});
expected_result.push(Opcode{
tag: "replace".to_string(),
first_start: 3,
first_end: 4,
second_start: 2,
second_end: 3
});
expected_result.push(Opcode{
tag: "equal".to_string(),
first_start: 4,
first_end: 6,
second_start: 3,
second_end: 5
});
expected_result.push(Opcode{
tag: "insert".to_string(),
first_start: 6,
first_end: 6,
second_start: 5,
second_end: 6
});
assert_eq!(result, expected_result);
}
#[test]
fn test_ratio() {
let mut matcher = SequenceMatcher::new("abcd", "bcde");
assert_eq!(matcher.ratio(), 0.75);
let mut matcher = SequenceMatcher::new("abcd", "bcde");
assert_eq!(matcher.ratio(), 0.75);
}
#[test]
fn test_get_close_matches() {
let words = vec!["ape", "apple", "peach", "puppy"];
let result = difflib::get_close_matches("appel", words, 3, 0.6);
assert_eq!(result, vec!["apple", "ape"]);
let words = vec!["ape", "apple", "peach", "puppy"];
let result = difflib::get_close_matches("appel", words, 3, 0.6);
assert_eq!(result, vec!["apple", "ape"]);
}
#[test]
fn test_differ_compare() {
let first_text = vec!["one\n", "two\n", "three\n"];
let second_text = vec!["ore\n", "tree\n", "emu\n"];
let differ = Differ::new();
let result = differ.compare(&first_text, &second_text).join("");
assert_eq!(result, "- one\n? ^\n+ ore\n? ^\n- two\n- three\n? -\n+ tree\n+ emu\n");
let first_text = vec!["one\n", "two\n", "three\n"];
let second_text = vec!["ore\n", "tree\n", "emu\n"];
let differ = Differ::new();
let result = differ.compare(&first_text, &second_text).join("");
assert_eq!(result, "- one\n? ^\n+ ore\n? ^\n- two\n- three\n? -\n+ tree\n+ emu\n");
}
fn is_junk_char(ch: &str) -> bool {
if ch == " " || ch == "\t" {
return true
}
false
if ch == " " || ch == "\t" {
return true
}
false
}
#[test]
fn test_differ_compare_with_func() {
let first_text = vec!["one\n", "two\n", "three\n"];
let second_text = vec!["ore\n", "tree\n", "emu\n"];
let mut differ = Differ::new();
differ.char_junk = Some(is_junk_char);
let result = differ.compare(&first_text, &second_text).join("");
assert_eq!(result, "- one\n? ^\n+ ore\n? ^\n- two\n- three\n? -\n+ tree\n+ emu\n");
let first_text = vec!["one\n", "two\n", "three\n"];
let second_text = vec!["ore\n", "tree\n", "emu\n"];
let mut differ = Differ::new();
differ.char_junk = Some(is_junk_char);
let result = differ.compare(&first_text, &second_text).join("");
assert_eq!(result, "- one\n? ^\n+ ore\n? ^\n- two\n- three\n? -\n+ tree\n+ emu\n");
}
#[test]
fn test_differ_restore() {
let first_text = vec!["one\n", " two\n", "three\n"];
let second_text = vec!["ore\n", "tree\n", "emu\n"];
let differ = Differ::new();
let diff = differ.compare(&first_text, &second_text);
assert_eq!(first_text, Differ::restore(&diff, 1));
assert_eq!(second_text, Differ::restore(&diff, 2));
let first_text = vec!["one\n", " two\n", "three\n"];
let second_text = vec!["ore\n", "tree\n", "emu\n"];
let differ = Differ::new();
let diff = differ.compare(&first_text, &second_text);
assert_eq!(first_text, Differ::restore(&diff, 1));
assert_eq!(second_text, Differ::restore(&diff, 2));
}
#[test]
fn test_unified_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::unified_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 +1,4 @@+zero one-two-three+tree four");
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::unified_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 +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");
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");
}