my fork of dmp
Release v5
Anubhab Bandyopadhyay 10 months ago
parent 4626b18 · commit 25b47c5
-rw-r--r--CHANGELOG.md9
-rw-r--r--Cargo.toml2
-rw-r--r--README.md9
-rw-r--r--src/dmp.rs2
-rw-r--r--tests/test.rs145
5 files changed, 161 insertions, 6 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e96c82d..23d0fa0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,14 @@
# CHANGELOG.md
+## 0.5.0
+Features:
+
+ - `diff_x_index( .. )` API contributed by @acarl005
+
+Fix:
+
+ - Fix for [Issue](https://github.com/AnubhabB/diff-match-patch-rs/issues/14) by @aschamp-figma
+
## 0.4.1
Minor performance & cleanup to Diff:
diff --git a/Cargo.toml b/Cargo.toml
index 5c4dea9..20fd8f4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "diff-match-patch-rs"
-version = "0.4.1"
+version = "0.5.0"
edition = "2021"
authors = ["Anubhab Bandyopadhyay"]
homepage = "https://docs.rs/diff-match-patch-rs"
diff --git a/README.md b/README.md
index 143a18e..924ddc2 100644
--- a/README.md
+++ b/README.md
@@ -39,7 +39,7 @@ Benchmarks are maintained [diff-match-patch-bench repository](https://github.com
```toml
[dependencies]
-diff-match-patch-rs = "0.4.1"
+diff-match-patch-rs = "0.5.0"
```
### `Effitient` mode
@@ -166,7 +166,7 @@ fn main() -> Result<(), Error> {
### `Match` - fuzzy match of pattern in Text
```rust
-use diff_match_patch_rs::{DiffMatchPatch, Compat, Error, PatchInput};
+use diff_match_patch_rs::{DiffMatchPatch, Compat, Error, PatchInput, Efficient};
// This is the source text
const TXT: &str = "I am the very model of a modern Major-General, I've information on vegetable, animal, and mineral, 🚀👏👀";
@@ -175,12 +175,13 @@ const TXT: &str = "I am the very model of a modern Major-General, I've informati
const PATTERN: &str = " that berry ";
// Returns `location` of match if found, `None` if not found
-fn main() -> Option<usize> {
+fn main() {
let dmp = DiffMatchPatch::new();
// works with both `Efficient` and `Compat` modes
// `5` here is an approx location to find `nearby` matches
- dmp.match_main::<Efficient>(TXT, PATTERN, 5) // this should return Some(4)
+ let match_at = dmp.match_main::<Efficient>(TXT, PATTERN, 5); // this should return Some(4)
+ println!("Matched at: {match_at:?}");
}
```
diff --git a/src/dmp.rs b/src/dmp.rs
index 1a06603..0aab3d8 100644
--- a/src/dmp.rs
+++ b/src/dmp.rs
@@ -709,7 +709,7 @@ impl DiffMatchPatch {
// Do all setup before casting to isize
let mut v;
let (v_offset, v_len, v1, v2, old_len, new_len) = {
- let v_offset = (old.len() + new.len() + 1) / 2;
+ let v_offset = (old.len() + new.len()).div_ceil(2);
let v_len = v_offset * 2;
v = vec![-1 as Int; v_len * 2];
diff --git a/tests/test.rs b/tests/test.rs
index 5e96a5f..35cd52d 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -1357,3 +1357,148 @@ fn test_match_main() {
)
);
}
+
+// Test for Issue: https://github.com/AnubhabB/diff-match-patch-rs/issues/14
+#[test]
+fn test_patch_repeats_todo_import() -> Result<(), Error> {
+ let dmp = DiffMatchPatch::default();
+
+ // Create source and target strings
+ let source = r##"(
+import { useState, useEffect } from "react";
+import { v4 as uuidv4 } from "uuid";
+import { TodoInput } from "./components/TodoInput";
+import { TodoList, Todo } from "./components/TodoList";
+import { TodoFilter, FilterStatus } from "./components/TodoFilter";
+import { Card, CardHeader, CardTitle, CardContent } from "./components/ui/card";
+
+// Mock implementation of uuid since we don't have access to the actual package
+const mockUuid = (): string => {
+ return Math.random().toString(36).substring(2, 15) +
+ Math.random().toString(36).substring(2, 15);
+};
+
+export default function App() {
+ const [todos, setTodos] = useState<Todo[]>(() => {
+ // Try to load todos from localStorage on initial render
+ const savedTodos = localStorage.getItem("todos");
+ return savedTodos ? JSON.parse(savedTodos) : [
+ { id: mockUui)"##;
+
+ let target = r##"(
+import { useState, useEffect } from "react";
+import { TodoInput } from "./components/TodoInput";
+import { TodoList, Todo } from "./components/TodoList";
+import { TodoFilter, FilterStatus } from "./components/TodoFilter";
+import { Card, CardHeader, CardTitle, CardContent } from "./components/ui/card";
+
+// Mock implementation of uuid since we don't have access to the actual package
+const mockUuid = (): string => {
+ return Math.random().toString(36).substring(2, 15) +
+ Math.random().toString(36).substring(2, 15);
+};
+
+export default function App() {
+ const [todos, setTodos] = useState<Todo[]>(() => {
+ // Try to load todos from localStorage on initial render
+ const savedTodos = localStorage.getItem("todos");
+ return savedTodos ? JSON.parse(savedTodos) : [
+ { id: mockUuid(), text: "Learn React", completed: true },
+ { id: mockUuid(), text: "Build a todo app", completed: false },
+ { id: mockUuid(), text: "Deploy to production", completed: false },
+ ];
+ });
+
+ const [activeFilter, setActiveFilter] = useState<FilterStatus>("all");
+
+ // Save todos to localStorage whenever they change
+ useEffect(() => {
+ localStorage.setItem("todos", JSON.stringify(todos));
+ }, [todos]);
+
+ // Calculate counts for the filter component
+ const todoCount = {
+ all: todos.length,
+ active: todos.filter(todo => !todo.completed).length,
+ completed: todos.filter(todo => todo.completed).length,
+ };
+
+ // Add a new todo
+ const handleAddTodo = (text: string) => {
+ setTodos([
+ ...todos,
+ {
+ id: mockUuid(),
+ text,
+ completed: false,
+ },
+ ]);
+ };
+
+ // Toggle todo completion status
+ const handleToggleComplete = (id: string) => {
+ setTodos(
+ todos.map(todo =>
+ todo.id === id ? { ...todo, completed: !todo.completed } : todo
+ )
+ );
+ };
+
+ // Delete a todo
+ const handleDeleteTodo = (id: string) => {
+ setTodos(todos.filter(todo => todo.id !== id));
+ };
+
+ return (
+ <div className="min-h-screen bg-background flex items-center justify-center p-4">
+ <div className="w-full max-w-md">
+ <Card className="shadow-lg">
+ <CardHeader className="pb-3">
+ <CardTitle className="text-xl font-semibold text-center">Todo List</CardTitle>
+ </CardHeader>
+ <CardContent>
+ <TodoInput onAddTodo={handleAddTodo} />
+
+ <TodoList
+ todos={todos}
+ activeFilter={activeFilter}
+ onToggleComplete={handleToggleComplete}
+ onDelete={handleDeleteTodo}
+ />
+
+ {todos.length > 0 && (
+ <TodoFilter
+ activeFilter={activeFilter}
+ onFilterChange={setActiveFilter}
+ todoCount={todoCount}
+ />
+ )}
+ </CardContent>
+ </Card>
+ </div>
+ </div>
+ );
+}
+)"##;
+
+ // Create patches to transform source into target
+ let patches = dmp.patch_make(PatchInput::Texts::<Efficient>(source, target))?;
+
+ // Apply the patches to the source string
+ let (patched, results) = dmp.patch_apply(&patches, source)?;
+
+ // Verify all patches were applied successfully
+ assert!(results.iter().all(|&result| result));
+
+ // Verify the patched string equals the target
+ assert_eq!(target, patched);
+
+ // Also test with Compat mode
+ let patches_compat = dmp.patch_make(PatchInput::Texts::<Compat>(source, target))?;
+ let (patched_compat, results_compat) = dmp.patch_apply(&patches_compat, source)?;
+
+ assert!(results_compat.iter().all(|&result| result));
+ assert_eq!(target, patched_compat);
+
+ Ok(())
+}