Add ability to specify user-defined functions for matcher
This commit is contained in:
+8
-7
@@ -4,20 +4,21 @@ use std::cmp;
|
|||||||
|
|
||||||
|
|
||||||
pub struct Differ {
|
pub struct Differ {
|
||||||
linejunk: Option<String>,
|
pub line_junk: Option<fn(&str) -> bool>,
|
||||||
charjunk: Option<String>
|
pub char_junk: Option<fn(&str) -> bool>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Differ{
|
impl Differ{
|
||||||
pub fn new() -> Differ {
|
pub fn new() -> Differ {
|
||||||
Differ{
|
Differ{
|
||||||
linejunk: None,
|
line_junk: None,
|
||||||
charjunk: None
|
char_junk: None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compare<T: ?Sized + Sequence>(&self, first_sequence: &T, second_sequence: &T) -> Vec<String> {
|
pub fn compare<T: ?Sized + Sequence>(&self, first_sequence: &T, 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);
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
for opcode in matcher.get_opcodes(){
|
for opcode in matcher.get_opcodes(){
|
||||||
let mut gen = Vec::new();
|
let mut gen = Vec::new();
|
||||||
@@ -87,7 +88,7 @@ impl Differ{
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let mut cruncher = SequenceMatcher::new(first_sequence_str, second_sequence_str);
|
let mut cruncher = SequenceMatcher::new(first_sequence_str, second_sequence_str);
|
||||||
//cruncher.charjunk = self.charjunk;
|
cruncher.set_is_junk(self.char_junk);
|
||||||
if cruncher.ratio() > best_ratio{
|
if cruncher.ratio() > best_ratio{
|
||||||
best_ratio = cruncher.ratio();
|
best_ratio = cruncher.ratio();
|
||||||
best_i = i;
|
best_i = i;
|
||||||
@@ -97,7 +98,7 @@ 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, first_start, first_end, second_sequence, second_start, second_end).iter().cloned());
|
res.extend(self.plain_replace(first_sequence, first_start, first_end, second_sequence, second_start, second_end).iter().cloned());
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
best_i = eqi.unwrap();
|
best_i = eqi.unwrap();
|
||||||
@@ -111,7 +112,7 @@ impl Differ{
|
|||||||
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.charjunk = self.charjunk;
|
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, opcode.second_end - opcode.second_start);
|
let (first_length, second_length) = (opcode.first_end - opcode.first_start, opcode.second_end - opcode.second_start);
|
||||||
match opcode.tag.as_ref() {
|
match opcode.tag.as_ref() {
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ 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> {
|
||||||
if !(0.0 <= cutoff && cutoff <= 1.0) {
|
if !(0.0 <= cutoff && cutoff <= 1.0) {
|
||||||
//error
|
panic!("Cutoff must be greater than 0.0 and lower than 1.0");
|
||||||
}
|
}
|
||||||
let mut res: Vec<(f32, &str)> = Vec::new();
|
let mut res: Vec<(f32, &str)> = Vec::new();
|
||||||
let mut matcher = SequenceMatcher::new("", word);
|
let mut matcher = SequenceMatcher::new("", word);
|
||||||
|
|||||||
+24
-20
@@ -44,7 +44,6 @@ impl Opcode{
|
|||||||
pub trait Sequence: Debug {
|
pub trait Sequence: Debug {
|
||||||
fn len(&self) -> usize;
|
fn len(&self) -> usize;
|
||||||
fn at_index(&self, index: usize) -> Option<&str>;
|
fn at_index(&self, index: usize) -> Option<&str>;
|
||||||
fn slice(&self, start: usize, end: usize) -> Option<&str>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sequence for str {
|
impl Sequence for str {
|
||||||
@@ -52,20 +51,13 @@ impl Sequence for str {
|
|||||||
self.len()
|
self.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn slice(&self, start: usize, end: usize) -> Option<&str> {
|
|
||||||
if start >= end{
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
if start > self.len() || end > self.len(){
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
unsafe{
|
|
||||||
Some(self.slice_unchecked(start, end))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn at_index(&self, index: usize) -> Option<&str> {
|
fn at_index(&self, index: usize) -> Option<&str> {
|
||||||
self.slice(index, index + 1)
|
if index > self.len(){
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
unsafe{
|
||||||
|
Some(self.slice_unchecked(index, index + 1))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,10 +66,6 @@ impl<'a> Sequence for Vec<&'a str> {
|
|||||||
self.len()
|
self.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn slice(&self, start: usize, end: usize) -> Option<&str> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn at_index(&self, index: usize) -> Option<&str> {
|
fn at_index(&self, index: usize) -> Option<&str> {
|
||||||
if index < self.len() && index >= 0 {
|
if index < self.len() && index >= 0 {
|
||||||
return Some(self[index])
|
return Some(self[index])
|
||||||
@@ -91,7 +79,7 @@ pub struct SequenceMatcher<'a, T: 'a + ?Sized + Sequence>{
|
|||||||
second_sequence: &'a T,
|
second_sequence: &'a T,
|
||||||
matching_blocks: Option<Vec<Match>>,
|
matching_blocks: Option<Vec<Match>>,
|
||||||
opcodes: Option<Vec<Opcode>>,
|
opcodes: Option<Vec<Opcode>>,
|
||||||
autojunk: bool,
|
is_junk: Option<fn(&str) -> bool>,
|
||||||
second_sequence_elements: HashMap<&'a str, Vec<usize>>,
|
second_sequence_elements: HashMap<&'a str, Vec<usize>>,
|
||||||
second_sequence_popular: Vec<&'a str>
|
second_sequence_popular: Vec<&'a str>
|
||||||
}
|
}
|
||||||
@@ -105,7 +93,7 @@ impl<'a, T: ?Sized + Sequence> SequenceMatcher<'a, T>{
|
|||||||
second_sequence: second_sequence,
|
second_sequence: second_sequence,
|
||||||
matching_blocks: None,
|
matching_blocks: None,
|
||||||
opcodes: None,
|
opcodes: None,
|
||||||
autojunk: true,
|
is_junk: None,
|
||||||
second_sequence_elements: HashMap::new(),
|
second_sequence_elements: HashMap::new(),
|
||||||
second_sequence_popular: Vec::new()
|
second_sequence_popular: Vec::new()
|
||||||
};
|
};
|
||||||
@@ -113,6 +101,11 @@ impl<'a, T: ?Sized + Sequence> SequenceMatcher<'a, T>{
|
|||||||
matcher
|
matcher
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_is_junk(&mut self, is_junk: Option<fn(&str) -> bool>) {
|
||||||
|
self.is_junk = is_junk;
|
||||||
|
self.set_second_seq(self.second_sequence);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_seqs(&mut self, first_sequence: &'a T, second_sequence: &'a T) {
|
pub fn set_seqs(&mut self, first_sequence: &'a T, second_sequence: &'a T) {
|
||||||
self.set_first_seq(first_sequence);
|
self.set_first_seq(first_sequence);
|
||||||
self.set_second_seq(second_sequence);
|
self.set_second_seq(second_sequence);
|
||||||
@@ -138,6 +131,17 @@ impl<'a, T: ?Sized + Sequence> SequenceMatcher<'a, T>{
|
|||||||
let mut counter = second_sequence_elements.entry(second_sequence.at_index(i).unwrap()).or_insert(Vec::new());
|
let mut counter = second_sequence_elements.entry(second_sequence.at_index(i).unwrap()).or_insert(Vec::new());
|
||||||
counter.push(i);
|
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 mut popular = Vec::new();
|
||||||
let len = second_sequence.len();
|
let len = second_sequence.len();
|
||||||
if len >= 200 {
|
if len >= 200 {
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ pub fn count_leading(line: &str, c: char) -> usize {
|
|||||||
|
|
||||||
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;
|
||||||
println!("{:?} {}", start, end);
|
|
||||||
let length = end - start;
|
let length = end - start;
|
||||||
if length == 1 {
|
if length == 1 {
|
||||||
return beginning.to_string()
|
return beginning.to_string()
|
||||||
|
|||||||
+18
-1
@@ -102,9 +102,26 @@ fn test_differ_compare() {
|
|||||||
assert_eq!(result, "- one\n? ^\n+ ore\n? ^\n- two\n- three\n? -\n+ tree\n+ emu\n");
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_differ_restore() {
|
fn test_differ_restore() {
|
||||||
let first_text = vec!["one\n", "two\n", "three\n"];
|
let first_text = vec!["one\n", " two\n", "three\n"];
|
||||||
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 diff = differ.compare(&first_text, &second_text);
|
let diff = differ.compare(&first_text, &second_text);
|
||||||
|
|||||||
Reference in New Issue
Block a user