Run cargo fmt
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# Difflib [](https://travis-ci.org/DimaKudosh/difflib)
|
# Difflib [](https://travis-ci.org/DimaKudosh/difflib)
|
||||||
|
|
||||||
Port of Python's difflib library to Rust.
|
Port of Python's difflib library to Rust.
|
||||||
It's provide all necessary tools for comparing word sequences.
|
It's provide all necessary tools for comparing word sequences.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
@@ -21,20 +21,33 @@ extern crate difflib;
|
|||||||
use difflib::differ::Differ;
|
use difflib::differ::Differ;
|
||||||
use difflib::sequencematcher::SequenceMatcher;
|
use difflib::sequencematcher::SequenceMatcher;
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// unified_diff
|
// unified_diff
|
||||||
let first_text = "one two three four".split(" ").collect::<Vec<&str>>();
|
let first_text = "one two three four".split(" ").collect::<Vec<&str>>();
|
||||||
let second_text = "zero one tree 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",
|
let diff = difflib::unified_diff(
|
||||||
"2005-01-26 23:30:50", "2010-04-02 10:20:52", 3);
|
&first_text,
|
||||||
|
&second_text,
|
||||||
|
"Original",
|
||||||
|
"Current",
|
||||||
|
"2005-01-26 23:30:50",
|
||||||
|
"2010-04-02 10:20:52",
|
||||||
|
3,
|
||||||
|
);
|
||||||
for line in &diff {
|
for line in &diff {
|
||||||
println!("{:?}", line);
|
println!("{:?}", line);
|
||||||
}
|
}
|
||||||
|
|
||||||
//context_diff
|
//context_diff
|
||||||
let diff = difflib::context_diff(&first_text, &second_text, "Original", "Current",
|
let diff = difflib::context_diff(
|
||||||
"2005-01-26 23:30:50", "2010-04-02 10:20:52", 3);
|
&first_text,
|
||||||
|
&second_text,
|
||||||
|
"Original",
|
||||||
|
"Current",
|
||||||
|
"2005-01-26 23:30:50",
|
||||||
|
"2010-04-02 10:20:52",
|
||||||
|
3,
|
||||||
|
);
|
||||||
for line in &diff {
|
for line in &diff {
|
||||||
println!("{:?}", line);
|
println!("{:?}", line);
|
||||||
}
|
}
|
||||||
@@ -62,7 +75,7 @@ fn main() {
|
|||||||
let grouped_opcodes = matcher.get_grouped_opcodes(2);
|
let grouped_opcodes = matcher.get_grouped_opcodes(2);
|
||||||
println!("{:?}", grouped_opcodes);
|
println!("{:?}", grouped_opcodes);
|
||||||
let ratio = matcher.ratio();
|
let ratio = matcher.ratio();
|
||||||
println!("{:?}", ratio);
|
println!("{:?}", ratio);
|
||||||
matcher.set_seqs("aaaaa", "aaaab");
|
matcher.set_seqs("aaaaa", "aaaab");
|
||||||
println!("{:?}", matcher.ratio());
|
println!("{:?}", matcher.ratio());
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-15
@@ -3,30 +3,33 @@ extern crate difflib;
|
|||||||
use difflib::differ::Differ;
|
use difflib::differ::Differ;
|
||||||
use difflib::sequencematcher::SequenceMatcher;
|
use difflib::sequencematcher::SequenceMatcher;
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// unified_diff
|
// unified_diff
|
||||||
let first_text = "one two three four".split(" ").collect::<Vec<&str>>();
|
let first_text = "one two three four".split(" ").collect::<Vec<&str>>();
|
||||||
let second_text = "zero one tree four".split(" ").collect::<Vec<&str>>();
|
let second_text = "zero one tree four".split(" ").collect::<Vec<&str>>();
|
||||||
let diff = difflib::unified_diff(&first_text,
|
let diff = difflib::unified_diff(
|
||||||
&second_text,
|
&first_text,
|
||||||
"Original",
|
&second_text,
|
||||||
"Current",
|
"Original",
|
||||||
"2005-01-26 23:30:50",
|
"Current",
|
||||||
"2010-04-02 10:20:52",
|
"2005-01-26 23:30:50",
|
||||||
3);
|
"2010-04-02 10:20:52",
|
||||||
|
3,
|
||||||
|
);
|
||||||
for line in &diff {
|
for line in &diff {
|
||||||
println!("{:?}", line);
|
println!("{:?}", line);
|
||||||
}
|
}
|
||||||
|
|
||||||
//context_diff
|
//context_diff
|
||||||
let diff = difflib::context_diff(&first_text,
|
let diff = difflib::context_diff(
|
||||||
&second_text,
|
&first_text,
|
||||||
"Original",
|
&second_text,
|
||||||
"Current",
|
"Original",
|
||||||
"2005-01-26 23:30:50",
|
"Current",
|
||||||
"2010-04-02 10:20:52",
|
"2005-01-26 23:30:50",
|
||||||
3);
|
"2010-04-02 10:20:52",
|
||||||
|
3,
|
||||||
|
);
|
||||||
for line in &diff {
|
for line in &diff {
|
||||||
println!("{:?}", line);
|
println!("{:?}", line);
|
||||||
}
|
}
|
||||||
|
|||||||
+145
-101
@@ -1,7 +1,6 @@
|
|||||||
use sequencematcher::{SequenceMatcher, Sequence};
|
use sequencematcher::{Sequence, SequenceMatcher};
|
||||||
use utils::{str_with_similar_chars, count_leading};
|
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
use utils::{count_leading, str_with_similar_chars};
|
||||||
|
|
||||||
pub struct Differ {
|
pub struct Differ {
|
||||||
pub line_junk: Option<fn(&str) -> bool>,
|
pub line_junk: Option<fn(&str) -> bool>,
|
||||||
@@ -16,10 +15,11 @@ impl Differ {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compare<T: ?Sized + Sequence>(&self,
|
pub fn compare<T: ?Sized + Sequence>(
|
||||||
first_sequence: &T,
|
&self,
|
||||||
second_sequence: &T)
|
first_sequence: &T,
|
||||||
-> Vec<String> {
|
second_sequence: &T,
|
||||||
|
) -> Vec<String> {
|
||||||
let mut matcher = SequenceMatcher::new(first_sequence, second_sequence);
|
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();
|
let mut res = Vec::new();
|
||||||
@@ -27,12 +27,14 @@ impl Differ {
|
|||||||
let mut gen = Vec::new();
|
let mut gen = Vec::new();
|
||||||
match opcode.tag.as_ref() {
|
match opcode.tag.as_ref() {
|
||||||
"replace" => {
|
"replace" => {
|
||||||
gen = self.fancy_replace(first_sequence,
|
gen = self.fancy_replace(
|
||||||
opcode.first_start,
|
first_sequence,
|
||||||
opcode.first_end,
|
opcode.first_start,
|
||||||
second_sequence,
|
opcode.first_end,
|
||||||
opcode.second_start,
|
second_sequence,
|
||||||
opcode.second_end)
|
opcode.second_start,
|
||||||
|
opcode.second_end,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
"delete" => {
|
"delete" => {
|
||||||
gen = self.dump("-", first_sequence, opcode.first_start, opcode.first_end)
|
gen = self.dump("-", first_sequence, opcode.first_start, opcode.first_end)
|
||||||
@@ -52,12 +54,13 @@ impl Differ {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dump<T: ?Sized + Sequence>(&self,
|
fn dump<T: ?Sized + Sequence>(
|
||||||
tag: &str,
|
&self,
|
||||||
sequence: &T,
|
tag: &str,
|
||||||
start: usize,
|
sequence: &T,
|
||||||
end: usize)
|
start: usize,
|
||||||
-> Vec<String> {
|
end: usize,
|
||||||
|
) -> Vec<String> {
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
for i in start..end {
|
for i in start..end {
|
||||||
match sequence.at_index(i) {
|
match sequence.at_index(i) {
|
||||||
@@ -68,14 +71,15 @@ impl Differ {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn plain_replace<T: ?Sized + Sequence>(&self,
|
fn plain_replace<T: ?Sized + Sequence>(
|
||||||
first_sequence: &T,
|
&self,
|
||||||
first_start: usize,
|
first_sequence: &T,
|
||||||
first_end: usize,
|
first_start: usize,
|
||||||
second_sequence: &T,
|
first_end: usize,
|
||||||
second_start: usize,
|
second_sequence: &T,
|
||||||
second_end: usize)
|
second_start: usize,
|
||||||
-> Vec<String> {
|
second_end: usize,
|
||||||
|
) -> Vec<String> {
|
||||||
if !(first_start < first_end && second_start < second_end) {
|
if !(first_start < first_end && second_start < second_end) {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
@@ -94,14 +98,15 @@ impl Differ {
|
|||||||
first
|
first
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fancy_replace<T: ?Sized + Sequence>(&self,
|
fn fancy_replace<T: ?Sized + Sequence>(
|
||||||
first_sequence: &T,
|
&self,
|
||||||
first_start: usize,
|
first_sequence: &T,
|
||||||
first_end: usize,
|
first_start: usize,
|
||||||
second_sequence: &T,
|
first_end: usize,
|
||||||
second_start: usize,
|
second_sequence: &T,
|
||||||
second_end: usize)
|
second_start: usize,
|
||||||
-> Vec<String> {
|
second_end: usize,
|
||||||
|
) -> Vec<String> {
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
let (mut best_ratio, cutoff) = (0.74, 0.75);
|
let (mut best_ratio, cutoff) = (0.74, 0.75);
|
||||||
let (mut best_i, mut best_j) = (0, 0);
|
let (mut best_i, mut best_j) = (0, 0);
|
||||||
@@ -130,14 +135,17 @@ impl Differ {
|
|||||||
}
|
}
|
||||||
if best_ratio < cutoff {
|
if best_ratio < cutoff {
|
||||||
if eqi.is_none() {
|
if eqi.is_none() {
|
||||||
res.extend(self.plain_replace(first_sequence,
|
res.extend(
|
||||||
first_start,
|
self.plain_replace(
|
||||||
first_end,
|
first_sequence,
|
||||||
second_sequence,
|
first_start,
|
||||||
second_start,
|
first_end,
|
||||||
second_end)
|
second_sequence,
|
||||||
.iter()
|
second_start,
|
||||||
.cloned());
|
second_end,
|
||||||
|
).iter()
|
||||||
|
.cloned(),
|
||||||
|
);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
best_i = eqi.unwrap();
|
best_i = eqi.unwrap();
|
||||||
@@ -145,23 +153,30 @@ impl Differ {
|
|||||||
} else {
|
} else {
|
||||||
eqi = None;
|
eqi = None;
|
||||||
}
|
}
|
||||||
res.extend(self.fancy_helper(first_sequence,
|
res.extend(
|
||||||
first_start,
|
self.fancy_helper(
|
||||||
best_i,
|
first_sequence,
|
||||||
second_sequence,
|
first_start,
|
||||||
second_start,
|
best_i,
|
||||||
best_j)
|
second_sequence,
|
||||||
.iter()
|
second_start,
|
||||||
.cloned());
|
best_j,
|
||||||
let (first_element, second_element) = (first_sequence.at_index(best_i).unwrap(),
|
).iter()
|
||||||
second_sequence.at_index(best_j).unwrap());
|
.cloned(),
|
||||||
|
);
|
||||||
|
let (first_element, second_element) = (
|
||||||
|
first_sequence.at_index(best_i).unwrap(),
|
||||||
|
second_sequence.at_index(best_j).unwrap(),
|
||||||
|
);
|
||||||
if eqi.is_none() {
|
if eqi.is_none() {
|
||||||
let (mut first_tag, mut second_tag) = (String::new(), String::new());
|
let (mut first_tag, mut second_tag) = (String::new(), String::new());
|
||||||
let mut cruncher = SequenceMatcher::new(first_element, second_element);
|
let mut cruncher = SequenceMatcher::new(first_element, second_element);
|
||||||
cruncher.set_is_junk(self.char_junk);
|
cruncher.set_is_junk(self.char_junk);
|
||||||
for opcode in &cruncher.get_opcodes() {
|
for opcode in &cruncher.get_opcodes() {
|
||||||
let (first_length, second_length) = (opcode.first_end - opcode.first_start,
|
let (first_length, second_length) = (
|
||||||
opcode.second_end - opcode.second_start);
|
opcode.first_end - opcode.first_start,
|
||||||
|
opcode.second_end - opcode.second_start,
|
||||||
|
);
|
||||||
match opcode.tag.as_ref() {
|
match opcode.tag.as_ref() {
|
||||||
"replace" => {
|
"replace" => {
|
||||||
first_tag.push_str(&str_with_similar_chars('^', first_length));
|
first_tag.push_str(&str_with_similar_chars('^', first_length));
|
||||||
@@ -180,42 +195,50 @@ impl Differ {
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res.extend(self.qformat(&first_element, &second_element, &first_tag, &second_tag)
|
res.extend(
|
||||||
.iter()
|
self.qformat(&first_element, &second_element, &first_tag, &second_tag)
|
||||||
.cloned());
|
.iter()
|
||||||
|
.cloned(),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
let mut s = String::from(" ");
|
let mut s = String::from(" ");
|
||||||
s.push_str(&first_element);
|
s.push_str(&first_element);
|
||||||
res.push(s);
|
res.push(s);
|
||||||
}
|
}
|
||||||
res.extend(self.fancy_helper(first_sequence,
|
res.extend(
|
||||||
best_i + 1,
|
self.fancy_helper(
|
||||||
first_end,
|
first_sequence,
|
||||||
second_sequence,
|
best_i + 1,
|
||||||
best_j + 1,
|
first_end,
|
||||||
second_end)
|
second_sequence,
|
||||||
.iter()
|
best_j + 1,
|
||||||
.cloned());
|
second_end,
|
||||||
|
).iter()
|
||||||
|
.cloned(),
|
||||||
|
);
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fancy_helper<T: ?Sized + Sequence>(&self,
|
fn fancy_helper<T: ?Sized + Sequence>(
|
||||||
first_sequence: &T,
|
&self,
|
||||||
first_start: usize,
|
first_sequence: &T,
|
||||||
first_end: usize,
|
first_start: usize,
|
||||||
second_sequence: &T,
|
first_end: usize,
|
||||||
second_start: usize,
|
second_sequence: &T,
|
||||||
second_end: usize)
|
second_start: usize,
|
||||||
-> Vec<String> {
|
second_end: usize,
|
||||||
|
) -> Vec<String> {
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
if first_start < first_end {
|
if first_start < first_end {
|
||||||
if second_start < second_end {
|
if second_start < second_end {
|
||||||
res = self.fancy_replace(first_sequence,
|
res = self.fancy_replace(
|
||||||
first_start,
|
first_sequence,
|
||||||
first_end,
|
first_start,
|
||||||
second_sequence,
|
first_end,
|
||||||
second_start,
|
second_sequence,
|
||||||
second_end);
|
second_start,
|
||||||
|
second_end,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
res = self.dump("-", first_sequence, first_start, first_end);
|
res = self.dump("-", first_sequence, first_start, first_end);
|
||||||
}
|
}
|
||||||
@@ -225,17 +248,20 @@ impl Differ {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn qformat(&self,
|
fn qformat(
|
||||||
first_line: &str,
|
&self,
|
||||||
second_line: &str,
|
first_line: &str,
|
||||||
first_tags: &str,
|
second_line: &str,
|
||||||
second_tags: &str)
|
first_tags: &str,
|
||||||
-> Vec<String> {
|
second_tags: &str,
|
||||||
|
) -> Vec<String> {
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
let mut first_tags = first_tags;
|
let mut first_tags = first_tags;
|
||||||
let mut second_tags = second_tags;
|
let mut second_tags = second_tags;
|
||||||
let mut common = cmp::min(count_leading(first_line, '\t'),
|
let mut common = cmp::min(
|
||||||
count_leading(second_line, '\t'));
|
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, ' '));
|
||||||
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();
|
first_tags = first_tags.split_at(common).1.trim_right();
|
||||||
@@ -243,15 +269,21 @@ impl Differ {
|
|||||||
let mut s = String::from(format!("- {}", first_line));
|
let mut s = String::from(format!("- {}", first_line));
|
||||||
res.push(s);
|
res.push(s);
|
||||||
if first_tags != "" {
|
if first_tags != "" {
|
||||||
s = String::from(format!("? {}{}\n", str_with_similar_chars('\t', common), first_tags));
|
s = String::from(format!(
|
||||||
|
"? {}{}\n",
|
||||||
|
str_with_similar_chars('\t', common),
|
||||||
|
first_tags
|
||||||
|
));
|
||||||
res.push(s);
|
res.push(s);
|
||||||
}
|
}
|
||||||
s = String::from(format!("+ {}", second_line));
|
s = String::from(format!("+ {}", second_line));
|
||||||
res.push(s);
|
res.push(s);
|
||||||
if second_tags != "" {
|
if second_tags != "" {
|
||||||
s = String::from(format!("? {}{}\n",
|
s = String::from(format!(
|
||||||
str_with_similar_chars('\t', common),
|
"? {}{}\n",
|
||||||
second_tags));
|
str_with_similar_chars('\t', common),
|
||||||
|
second_tags
|
||||||
|
));
|
||||||
res.push(s);
|
res.push(s);
|
||||||
}
|
}
|
||||||
res
|
res
|
||||||
@@ -283,19 +315,31 @@ impl Differ {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_fancy_replace() {
|
fn test_fancy_replace() {
|
||||||
let differ = Differ::new();
|
let differ = Differ::new();
|
||||||
let result = differ.fancy_replace(&vec!["abcDefghiJkl\n"], 0, 1, &vec!["abcdefGhijkl\n"], 0, 1)
|
let result = differ
|
||||||
|
.fancy_replace(&vec!["abcDefghiJkl\n"], 0, 1, &vec!["abcdefGhijkl\n"], 0, 1)
|
||||||
.join("");
|
.join("");
|
||||||
assert_eq!(result,
|
assert_eq!(
|
||||||
"- abcDefghiJkl\n? ^ ^ ^\n+ abcdefGhijkl\n? ^ ^ ^\n");
|
result,
|
||||||
|
"- abcDefghiJkl\n? ^ ^ ^\n+ abcdefGhijkl\n? ^ ^ ^\n"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_qformat() {
|
fn test_qformat() {
|
||||||
let differ = Differ::new();
|
let differ = Differ::new();
|
||||||
let result = differ.qformat("\tabcDefghiJkl\n",
|
let result = differ.qformat(
|
||||||
"\tabcdefGhijkl\n",
|
"\tabcDefghiJkl\n",
|
||||||
" ^ ^ ^ ",
|
"\tabcdefGhijkl\n",
|
||||||
" ^ ^ ^ ");
|
" ^ ^ ^ ",
|
||||||
assert_eq!(result,
|
" ^ ^ ^ ",
|
||||||
vec!["- \tabcDefghiJkl\n", "? \t ^ ^ ^\n", "+ \tabcdefGhijkl\n", "? \t ^ ^ ^\n"]);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
vec![
|
||||||
|
"- \tabcDefghiJkl\n",
|
||||||
|
"? \t ^ ^ ^\n",
|
||||||
|
"+ \tabcdefGhijkl\n",
|
||||||
|
"? \t ^ ^ ^\n",
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+41
-34
@@ -1,18 +1,17 @@
|
|||||||
pub mod sequencematcher;
|
|
||||||
pub mod differ;
|
pub mod differ;
|
||||||
|
pub mod sequencematcher;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
|
use sequencematcher::{Sequence, SequenceMatcher};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use sequencematcher::{SequenceMatcher, Sequence};
|
use utils::{format_range_context, format_range_unified};
|
||||||
use utils::{format_range_unified, format_range_context};
|
|
||||||
|
|
||||||
|
pub fn get_close_matches<'a>(
|
||||||
pub fn get_close_matches<'a>(word: &str,
|
word: &str,
|
||||||
possibilities: Vec<&'a str>,
|
possibilities: Vec<&'a str>,
|
||||||
n: usize,
|
n: usize,
|
||||||
cutoff: f32)
|
cutoff: f32,
|
||||||
-> Vec<&'a str> {
|
) -> Vec<&'a str> {
|
||||||
if !(0.0 <= cutoff && cutoff <= 1.0) {
|
if !(0.0 <= cutoff && cutoff <= 1.0) {
|
||||||
panic!("Cutoff must be greater than 0.0 and lower than 1.0");
|
panic!("Cutoff must be greater than 0.0 and lower than 1.0");
|
||||||
}
|
}
|
||||||
@@ -30,14 +29,15 @@ pub fn get_close_matches<'a>(word: &str,
|
|||||||
res.iter().map(|x| x.1).collect()
|
res.iter().map(|x| x.1).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unified_diff<T: Sequence>(first_sequence: &T,
|
pub fn unified_diff<T: Sequence>(
|
||||||
second_sequence: &T,
|
first_sequence: &T,
|
||||||
from_file: &str,
|
second_sequence: &T,
|
||||||
to_file: &str,
|
from_file: &str,
|
||||||
from_file_date: &str,
|
to_file: &str,
|
||||||
to_file_date: &str,
|
from_file_date: &str,
|
||||||
n: usize)
|
to_file_date: &str,
|
||||||
-> Vec<String> {
|
n: usize,
|
||||||
|
) -> Vec<String> {
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
let lineterm = '\n';
|
let lineterm = '\n';
|
||||||
let mut started = false;
|
let mut started = false;
|
||||||
@@ -53,7 +53,10 @@ pub fn unified_diff<T: Sequence>(first_sequence: &T,
|
|||||||
let (first, last) = (group.first().unwrap(), group.last().unwrap());
|
let (first, last) = (group.first().unwrap(), group.last().unwrap());
|
||||||
let file1_range = format_range_unified(first.first_start, last.first_end);
|
let file1_range = format_range_unified(first.first_start, last.first_end);
|
||||||
let file2_range = format_range_unified(first.second_start, last.second_end);
|
let file2_range = format_range_unified(first.second_start, last.second_end);
|
||||||
res.push(format!("@@ -{} +{} @@{}", file1_range, file2_range, lineterm));
|
res.push(format!(
|
||||||
|
"@@ -{} +{} @@{}",
|
||||||
|
file1_range, file2_range, lineterm
|
||||||
|
));
|
||||||
for code in group {
|
for code in group {
|
||||||
if code.tag == "equal" {
|
if code.tag == "equal" {
|
||||||
for i in code.first_start..code.first_end {
|
for i in code.first_start..code.first_end {
|
||||||
@@ -76,15 +79,15 @@ pub fn unified_diff<T: Sequence>(first_sequence: &T,
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn context_diff<T: Sequence>(
|
||||||
pub fn context_diff<T: Sequence>(first_sequence: &T,
|
first_sequence: &T,
|
||||||
second_sequence: &T,
|
second_sequence: &T,
|
||||||
from_file: &str,
|
from_file: &str,
|
||||||
to_file: &str,
|
to_file: &str,
|
||||||
from_file_date: &str,
|
from_file_date: &str,
|
||||||
to_file_date: &str,
|
to_file_date: &str,
|
||||||
n: usize)
|
n: usize,
|
||||||
-> Vec<String> {
|
) -> Vec<String> {
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
let lineterm = '\n';
|
let lineterm = '\n';
|
||||||
let mut prefix: HashMap<String, String> = HashMap::new();
|
let mut prefix: HashMap<String, String> = HashMap::new();
|
||||||
@@ -117,9 +120,11 @@ pub fn context_diff<T: Sequence>(first_sequence: &T,
|
|||||||
for opcode in group {
|
for opcode in group {
|
||||||
if opcode.tag != "insert" {
|
if opcode.tag != "insert" {
|
||||||
for i in opcode.first_start..opcode.first_end {
|
for i in opcode.first_start..opcode.first_end {
|
||||||
res.push(format!("{}{}",
|
res.push(format!(
|
||||||
prefix.get(&opcode.tag).unwrap(),
|
"{}{}",
|
||||||
first_sequence.at_index(i).unwrap()));
|
prefix.get(&opcode.tag).unwrap(),
|
||||||
|
first_sequence.at_index(i).unwrap()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,9 +142,11 @@ pub fn context_diff<T: Sequence>(first_sequence: &T,
|
|||||||
for opcode in group {
|
for opcode in group {
|
||||||
if opcode.tag != "delete" {
|
if opcode.tag != "delete" {
|
||||||
for i in opcode.second_start..opcode.second_end {
|
for i in opcode.second_start..opcode.second_end {
|
||||||
res.push(format!("{}{}",
|
res.push(format!(
|
||||||
prefix.get(&opcode.tag).unwrap(),
|
"{}{}",
|
||||||
second_sequence.at_index(i).unwrap()));
|
prefix.get(&opcode.tag).unwrap(),
|
||||||
|
second_sequence.at_index(i).unwrap()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+60
-41
@@ -1,7 +1,7 @@
|
|||||||
use std::collections::{HashMap, HashSet};
|
|
||||||
use std::cmp::{max, min};
|
use std::cmp::{max, min};
|
||||||
use utils::calculate_ratio;
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
use utils::calculate_ratio;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
|
||||||
pub struct Match {
|
pub struct Match {
|
||||||
@@ -30,12 +30,13 @@ pub struct Opcode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Opcode {
|
impl Opcode {
|
||||||
fn new(tag: String,
|
fn new(
|
||||||
first_start: usize,
|
tag: String,
|
||||||
first_end: usize,
|
first_start: usize,
|
||||||
second_start: usize,
|
first_end: usize,
|
||||||
second_end: usize)
|
second_start: usize,
|
||||||
-> Opcode {
|
second_end: usize,
|
||||||
|
) -> Opcode {
|
||||||
Opcode {
|
Opcode {
|
||||||
tag: tag,
|
tag: tag,
|
||||||
first_start: first_start,
|
first_start: first_start,
|
||||||
@@ -113,7 +114,6 @@ pub struct SequenceMatcher<'a, T: 'a + ?Sized + Sequence> {
|
|||||||
second_sequence_popular: HashSet<&'a str>,
|
second_sequence_popular: HashSet<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<'a, T: ?Sized + Sequence> SequenceMatcher<'a, T> {
|
impl<'a, T: ?Sized + Sequence> SequenceMatcher<'a, T> {
|
||||||
pub fn new(first_sequence: &'a T, second_sequence: &'a T) -> SequenceMatcher<'a, T> {
|
pub fn new(first_sequence: &'a T, second_sequence: &'a T) -> SequenceMatcher<'a, T> {
|
||||||
let mut matcher = SequenceMatcher {
|
let mut matcher = SequenceMatcher {
|
||||||
@@ -156,7 +156,8 @@ impl<'a, T: ?Sized + Sequence> SequenceMatcher<'a, T> {
|
|||||||
let second_sequence = self.second_sequence;
|
let second_sequence = self.second_sequence;
|
||||||
let mut second_sequence_elements = HashMap::new();
|
let mut second_sequence_elements = HashMap::new();
|
||||||
for i in 0..second_sequence.len() {
|
for i in 0..second_sequence.len() {
|
||||||
let mut counter = second_sequence_elements.entry(second_sequence.at_index(i).unwrap())
|
let mut counter = second_sequence_elements
|
||||||
|
.entry(second_sequence.at_index(i).unwrap())
|
||||||
.or_insert(Vec::new());
|
.or_insert(Vec::new());
|
||||||
counter.push(i);
|
counter.push(i);
|
||||||
}
|
}
|
||||||
@@ -188,12 +189,13 @@ impl<'a, T: ?Sized + Sequence> SequenceMatcher<'a, T> {
|
|||||||
self.second_sequence_popular = popular;
|
self.second_sequence_popular = popular;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_longest_match(&self,
|
pub fn find_longest_match(
|
||||||
first_start: usize,
|
&self,
|
||||||
first_end: usize,
|
first_start: usize,
|
||||||
second_start: usize,
|
first_end: usize,
|
||||||
second_end: usize)
|
second_start: usize,
|
||||||
-> Match {
|
second_end: usize,
|
||||||
|
) -> Match {
|
||||||
let first_sequence = &self.first_sequence;
|
let first_sequence = &self.first_sequence;
|
||||||
let second_sequence = &self.second_sequence;
|
let second_sequence = &self.second_sequence;
|
||||||
let second_sequence_elements = &self.second_sequence_elements;
|
let second_sequence_elements = &self.second_sequence_elements;
|
||||||
@@ -234,15 +236,18 @@ impl<'a, T: ?Sized + Sequence> SequenceMatcher<'a, T> {
|
|||||||
j2len = new_j2len;
|
j2len = new_j2len;
|
||||||
}
|
}
|
||||||
for _ in 0..2 {
|
for _ in 0..2 {
|
||||||
while best_i > first_start && best_j > second_start &&
|
while best_i > first_start
|
||||||
first_sequence.at_index(best_i - 1) == second_sequence.at_index(best_j - 1) {
|
&& best_j > second_start
|
||||||
|
&& first_sequence.at_index(best_i - 1) == second_sequence.at_index(best_j - 1)
|
||||||
|
{
|
||||||
best_i = best_i - 1;
|
best_i = best_i - 1;
|
||||||
best_j = best_j - 1;
|
best_j = best_j - 1;
|
||||||
best_size = best_size + 1;
|
best_size = best_size + 1;
|
||||||
}
|
}
|
||||||
while best_i + best_size < first_end && best_j + best_size < second_end &&
|
while best_i + best_size < first_end && best_j + best_size < second_end
|
||||||
first_sequence.at_index(best_i + best_size) ==
|
&& first_sequence.at_index(best_i + best_size)
|
||||||
second_sequence.at_index(best_j + best_size) {
|
== second_sequence.at_index(best_j + best_size)
|
||||||
|
{
|
||||||
best_size += 1;
|
best_size += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -266,10 +271,12 @@ impl<'a, T: ?Sized + Sequence> SequenceMatcher<'a, T> {
|
|||||||
queue.push((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 {
|
if m.first_start + m.size < first_end && m.second_start + m.size < second_end {
|
||||||
queue.push((m.first_start + m.size,
|
queue.push((
|
||||||
first_end,
|
m.first_start + m.size,
|
||||||
m.second_start + m.size,
|
first_end,
|
||||||
second_end));
|
m.second_start + m.size,
|
||||||
|
second_end,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
matches.push(m);
|
matches.push(m);
|
||||||
}
|
}
|
||||||
@@ -319,9 +326,13 @@ impl<'a, T: ?Sized + Sequence> SequenceMatcher<'a, T> {
|
|||||||
i = m.first_start + m.size;
|
i = m.first_start + m.size;
|
||||||
j = m.second_start + m.size;
|
j = m.second_start + m.size;
|
||||||
if m.size != 0 {
|
if m.size != 0 {
|
||||||
opcodes.push(
|
opcodes.push(Opcode::new(
|
||||||
Opcode::new(String::from("equal"), m.first_start, i, m.second_start, j)
|
String::from("equal"),
|
||||||
);
|
m.first_start,
|
||||||
|
i,
|
||||||
|
m.second_start,
|
||||||
|
j,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.opcodes = Some(opcodes);
|
self.opcodes = Some(opcodes);
|
||||||
@@ -350,21 +361,25 @@ Opcode::new(String::from("equal"), m.first_start, i, m.second_start, j)
|
|||||||
for code in &codes {
|
for code in &codes {
|
||||||
let (mut first_start, mut second_start) = (code.first_start, code.second_start);
|
let (mut first_start, mut second_start) = (code.first_start, code.second_start);
|
||||||
if code.tag == "equal" && code.first_end - code.first_start > nn {
|
if code.tag == "equal" && code.first_end - code.first_start > nn {
|
||||||
group.push(Opcode::new(code.tag.clone(),
|
group.push(Opcode::new(
|
||||||
code.first_start,
|
code.tag.clone(),
|
||||||
min(code.first_end, code.first_start + n),
|
code.first_start,
|
||||||
code.second_start,
|
min(code.first_end, code.first_start + n),
|
||||||
min(code.second_end, code.second_start + n)));
|
code.second_start,
|
||||||
|
min(code.second_end, code.second_start + n),
|
||||||
|
));
|
||||||
res.push(group.clone());
|
res.push(group.clone());
|
||||||
group.clear();
|
group.clear();
|
||||||
first_start = max(first_start, code.first_end.saturating_sub(n));
|
first_start = max(first_start, code.first_end.saturating_sub(n));
|
||||||
second_start = max(second_start, code.second_end.saturating_sub(n));
|
second_start = max(second_start, code.second_end.saturating_sub(n));
|
||||||
}
|
}
|
||||||
group.push(Opcode::new(code.tag.clone(),
|
group.push(Opcode::new(
|
||||||
first_start,
|
code.tag.clone(),
|
||||||
code.first_end,
|
first_start,
|
||||||
second_start,
|
code.first_end,
|
||||||
code.second_end));
|
second_start,
|
||||||
|
code.second_end,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
if !group.is_empty() && !(group.len() == 1 && group.first().unwrap().tag == "equal") {
|
if !group.is_empty() && !(group.len() == 1 && group.first().unwrap().tag == "equal") {
|
||||||
res.push(group.clone());
|
res.push(group.clone());
|
||||||
@@ -373,8 +388,12 @@ Opcode::new(String::from("equal"), m.first_start, i, m.second_start, j)
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn ratio(&mut self) -> f32 {
|
pub fn ratio(&mut self) -> f32 {
|
||||||
let matches = self.get_matching_blocks().iter().fold(0, |res, &m| res + m.size);
|
let matches = self.get_matching_blocks()
|
||||||
calculate_ratio(matches,
|
.iter()
|
||||||
self.first_sequence.len() + self.second_sequence.len())
|
.fold(0, |res, &m| res + m.size);
|
||||||
|
calculate_ratio(
|
||||||
|
matches,
|
||||||
|
self.first_sequence.len() + self.second_sequence.len(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ pub fn calculate_ratio(matches: usize, length: usize) -> f32 {
|
|||||||
return 1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn str_with_similar_chars(c: char, length: usize) -> String {
|
pub fn str_with_similar_chars(c: char, length: usize) -> String {
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
for _ in 0..length {
|
for _ in 0..length {
|
||||||
@@ -14,7 +13,6 @@ pub fn str_with_similar_chars(c: char, length: usize) -> String {
|
|||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn count_leading(line: &str, c: char) -> usize {
|
pub fn count_leading(line: &str, c: char) -> usize {
|
||||||
let (mut i, n) = (0, line.len());
|
let (mut i, n) = (0, line.len());
|
||||||
let line: Vec<char> = line.chars().collect();
|
let line: Vec<char> = line.chars().collect();
|
||||||
@@ -24,7 +22,6 @@ pub fn count_leading(line: &str, c: char) -> usize {
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn format_range_unified(start: usize, end: usize) -> String {
|
pub fn format_range_unified(start: usize, end: usize) -> String {
|
||||||
let mut beginning = start + 1;
|
let mut beginning = start + 1;
|
||||||
let length = end - start;
|
let length = end - start;
|
||||||
@@ -37,7 +34,6 @@ pub fn format_range_unified(start: usize, end: usize) -> String {
|
|||||||
format!("{},{}", beginning, length)
|
format!("{},{}", beginning, length)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn format_range_context(start: usize, end: usize) -> String {
|
pub fn format_range_context(start: usize, end: usize) -> String {
|
||||||
let mut beginning = start + 1;
|
let mut beginning = start + 1;
|
||||||
let length = end - start;
|
let length = end - start;
|
||||||
|
|||||||
+38
-28
@@ -1,7 +1,7 @@
|
|||||||
extern crate difflib;
|
extern crate difflib;
|
||||||
|
|
||||||
use difflib::sequencematcher::{SequenceMatcher, Match, Opcode};
|
|
||||||
use difflib::differ::Differ;
|
use difflib::differ::Differ;
|
||||||
|
use difflib::sequencematcher::{Match, Opcode, SequenceMatcher};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_longest_match() {
|
fn test_longest_match() {
|
||||||
@@ -97,8 +97,10 @@ fn test_differ_compare() {
|
|||||||
let second_text = vec!["ore\n", "tree\n", "emu\n"];
|
let second_text = vec!["ore\n", "tree\n", "emu\n"];
|
||||||
let differ = Differ::new();
|
let differ = Differ::new();
|
||||||
let result = differ.compare(&first_text, &second_text).join("");
|
let result = differ.compare(&first_text, &second_text).join("");
|
||||||
assert_eq!(result,
|
assert_eq!(
|
||||||
"- one\n? ^\n+ ore\n? ^\n- two\n- three\n? -\n+ tree\n+ emu\n");
|
result,
|
||||||
|
"- one\n? ^\n+ ore\n? ^\n- two\n- three\n? -\n+ tree\n+ emu\n"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_junk_char(ch: &str) -> bool {
|
fn is_junk_char(ch: &str) -> bool {
|
||||||
@@ -115,8 +117,10 @@ fn test_differ_compare_with_func() {
|
|||||||
let mut differ = Differ::new();
|
let mut differ = Differ::new();
|
||||||
differ.char_junk = Some(is_junk_char);
|
differ.char_junk = Some(is_junk_char);
|
||||||
let result = differ.compare(&first_text, &second_text).join("");
|
let result = differ.compare(&first_text, &second_text).join("");
|
||||||
assert_eq!(result,
|
assert_eq!(
|
||||||
"- one\n? ^\n+ ore\n? ^\n- two\n- three\n? -\n+ tree\n+ emu\n");
|
result,
|
||||||
|
"- one\n? ^\n+ ore\n? ^\n- two\n- three\n? -\n+ tree\n+ emu\n"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -133,33 +137,39 @@ fn test_differ_restore() {
|
|||||||
fn test_unified_diff() {
|
fn test_unified_diff() {
|
||||||
let first_text = "one two three four".split(" ").collect::<Vec<&str>>();
|
let first_text = "one two three four".split(" ").collect::<Vec<&str>>();
|
||||||
let second_text = "zero one tree four".split(" ").collect::<Vec<&str>>();
|
let second_text = "zero one tree four".split(" ").collect::<Vec<&str>>();
|
||||||
let result = difflib::unified_diff(&first_text,
|
let result = difflib::unified_diff(
|
||||||
&second_text,
|
&first_text,
|
||||||
"Original",
|
&second_text,
|
||||||
"Current",
|
"Original",
|
||||||
"2005-01-26 23:30:50",
|
"Current",
|
||||||
"2010-04-02 10:20:52",
|
"2005-01-26 23:30:50",
|
||||||
3)
|
"2010-04-02 10:20:52",
|
||||||
.join("");
|
3,
|
||||||
assert_eq!(result,
|
).join("");
|
||||||
"--- Original\t2005-01-26 23:30:50\n+++ Current\t2010-04-02 10:20:52\n@@ -1,4 \
|
assert_eq!(
|
||||||
+1,4 @@\n+zero one-two-three+tree four");
|
result,
|
||||||
|
"--- Original\t2005-01-26 23:30:50\n+++ Current\t2010-04-02 10:20:52\n@@ -1,4 \
|
||||||
|
+1,4 @@\n+zero one-two-three+tree four"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_context_diff() {
|
fn test_context_diff() {
|
||||||
let first_text = "one two three four".split(" ").collect::<Vec<&str>>();
|
let first_text = "one two three four".split(" ").collect::<Vec<&str>>();
|
||||||
let second_text = "zero one tree four".split(" ").collect::<Vec<&str>>();
|
let second_text = "zero one tree four".split(" ").collect::<Vec<&str>>();
|
||||||
let result = difflib::context_diff(&first_text,
|
let result = difflib::context_diff(
|
||||||
&second_text,
|
&first_text,
|
||||||
"Original",
|
&second_text,
|
||||||
"Current",
|
"Original",
|
||||||
"2005-01-26 23:30:50",
|
"Current",
|
||||||
"2010-04-02 10:20:52",
|
"2005-01-26 23:30:50",
|
||||||
3)
|
"2010-04-02 10:20:52",
|
||||||
.join("");
|
3,
|
||||||
assert_eq!(result,
|
).join("");
|
||||||
"*** Original\t2005-01-26 23:30:50\n--- Current\t2010-04-02 \
|
assert_eq!(
|
||||||
10:20:52\n***************\n*** 1,4 ****\n one! two! three four--- 1,4 ----\n+ \
|
result,
|
||||||
zero one! tree four");
|
"*** Original\t2005-01-26 23:30:50\n--- Current\t2010-04-02 \
|
||||||
|
10:20:52\n***************\n*** 1,4 ****\n one! two! three four--- 1,4 ----\n+ \
|
||||||
|
zero one! tree four"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user