aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2021-09-21 22:43:20 +0200
committerMel <einebeere@gmail.com>2021-09-21 22:43:20 +0200
commit4f954112b8c8cf266a8338cc86166f912c2da88c (patch)
treedee4e22a7b6cf8d49ae582e9104bf406c0bc9b3b
parentb86138b138565178b916b24d1e30d5786fd9d1c5 (diff)
downloadka-4f954112b8c8cf266a8338cc86166f912c2da88c.tar.zst
ka-4f954112b8c8cf266a8338cc86166f912c2da88c.zip
Byte differ
-rw-r--r--Cargo.lock14
-rw-r--r--Cargo.toml2
-rw-r--r--src/actions/shift.rs4
-rw-r--r--src/actions/update.rs14
-rw-r--r--src/diff.rs127
-rw-r--r--src/history.rs10
-rw-r--r--src/lib.rs2
-rw-r--r--src/text_diff.rs52
8 files changed, 150 insertions, 75 deletions
diff --git a/Cargo.lock b/Cargo.lock
index b22eca6..e51a480 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -9,12 +9,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1"
[[package]]
-name = "difference"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
-
-[[package]]
name = "itoa"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -25,9 +19,9 @@ name = "ka"
version = "0.1.0"
dependencies = [
"anyhow",
- "difference",
"serde",
"serde_json",
+ "similar",
]
[[package]]
@@ -93,6 +87,12 @@ dependencies = [
]
[[package]]
+name = "similar"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bf11003835e462f07851028082d2a1c89d956180ce4b4b50e07fb085ec4131a"
+
+[[package]]
name = "syn"
version = "1.0.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 7605ee0..9d715e6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,7 +8,7 @@ edition = "2018"
[dependencies]
serde = {version = "1.0", features = ["derive"] }
serde_json = "1.0"
-difference = "2.0.0"
+similar = "2.0.0"
anyhow = "1.0"
[workspace]
diff --git a/src/actions/shift.rs b/src/actions/shift.rs
index 437fcb4..6880c20 100644
--- a/src/actions/shift.rs
+++ b/src/actions/shift.rs
@@ -32,7 +32,7 @@ pub fn shift(command_options: ActionOptions, path: &str, new_cursor: usize) -> R
working_file.rewind()?;
working_file.set_len(0)?;
- working_file.write_all(new_content.as_bytes())?;
+ working_file.write_all(&new_content)?;
}
file_history.write_to_file(&mut history_file)?;
@@ -47,7 +47,7 @@ pub fn shift(command_options: ActionOptions, path: &str, new_cursor: usize) -> R
let mut new_working_file = deleted.create_working_file(&locations)?;
let new_content = file_history.get_content();
- new_working_file.write_all(new_content.as_bytes())?;
+ new_working_file.write_all(&new_content)?;
}
}
FileState::Untracked(_) => panic!("File is not tracked with Ka."),
diff --git a/src/actions/update.rs b/src/actions/update.rs
index 73a9222..aa9c3a4 100644
--- a/src/actions/update.rs
+++ b/src/actions/update.rs
@@ -5,7 +5,7 @@ use anyhow::{Context, Result};
use crate::{
files::{Locations, FileState},
history::{FileChange, FileHistory},
- text_diff::TextChange,
+ diff::ContentChange,
};
use super::ActionOptions;
@@ -40,10 +40,10 @@ fn update_file(file_state: FileState, locations: &Locations) -> Result<()> {
FileState::Untracked(untracked) => {
let mut file = untracked.load_file()?;
- let mut file_content = String::new();
- file.read_to_string(&mut file_content)?;
+ let mut file_content = Vec::new();
+ file.read_to_end(&mut file_content)?;
- let change = FileChange::Updated(vec![TextChange::Inserted {
+ let change = FileChange::Updated(vec![ContentChange::Inserted {
at: 0,
new_content: file_content,
}]);
@@ -59,12 +59,12 @@ fn update_file(file_state: FileState, locations: &Locations) -> Result<()> {
let file_history = FileHistory::from_file(&mut history_file)?;
- let mut new_content = String::new();
- working_file.read_to_string(&mut new_content)?;
+ let mut new_content = Vec::new();
+ working_file.read_to_end(&mut new_content)?;
let old_content = file_history.get_content();
- let changes = TextChange::diff(&old_content, &new_content);
+ let changes = ContentChange::diff(&old_content, &new_content);
let mut new_history = file_history;
new_history.add_change(FileChange::Updated(changes));
diff --git a/src/diff.rs b/src/diff.rs
new file mode 100644
index 0000000..4e3531c
--- /dev/null
+++ b/src/diff.rs
@@ -0,0 +1,127 @@
+use std::time::{Duration, Instant};
+
+use serde::{Deserialize, Serialize};
+use similar::{Algorithm, DiffOp};
+
+#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
+pub enum ContentChange {
+ Inserted { at: usize, new_content: Vec<u8> },
+ Deleted { at: usize, upto: usize },
+}
+
+impl ContentChange {
+ pub fn diff(old: &[u8], new: &[u8]) -> Vec<Self> {
+ let deadline = Instant::now() + Duration::from_millis(100);
+ let change_set =
+ similar::capture_diff_slices_deadline(Algorithm::Myers, old, new, Some(deadline));
+
+ let mut at = 0;
+ let mut changes = Vec::new();
+
+ for diff in change_set {
+ match diff {
+ DiffOp::Delete { old_len, .. } => {
+ changes.push(ContentChange::Deleted {
+ at,
+ upto: at + old_len,
+ });
+ }
+ DiffOp::Insert {
+ new_index, new_len, ..
+ } => {
+ let new_content = &new[new_index..new_index + new_len];
+ let change = ContentChange::Inserted {
+ at,
+ new_content: new_content.to_vec(),
+ };
+ at += new_len;
+ changes.push(change);
+ }
+ DiffOp::Replace {
+ old_len,
+ new_index,
+ new_len,
+ ..
+ } => {
+ let new_content = &new[new_index..new_index + new_len];
+
+ let removed_change = ContentChange::Deleted {
+ at,
+ upto: at + old_len,
+ };
+ let added_change = ContentChange::Inserted {
+ at,
+ new_content: new_content.to_vec(),
+ };
+
+ changes.push(removed_change);
+ changes.push(added_change);
+
+ at += new_len;
+ }
+ DiffOp::Equal { len, .. } => {
+ at += len;
+ }
+ }
+ }
+
+ changes
+ }
+
+ pub fn apply(&self, buffer: &mut Vec<u8>) {
+ match self {
+ ContentChange::Deleted { at, upto } => {
+ buffer.drain(at..upto);
+ }
+ ContentChange::Inserted { at, new_content } => {
+ buffer.splice(at..at, new_content.clone());
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::{ContentChange::*, *};
+
+ #[test]
+ fn test_diff() {
+ let old = "This is an old string...";
+ let new = "This is a new string...!";
+
+ let changes = ContentChange::diff(old.as_bytes(), new.as_bytes());
+ assert_eq!(
+ changes.as_slice(),
+ [
+ Inserted {
+ at: 9,
+ new_content: " ".into()
+ },
+ Deleted { at: 11, upto: 15 },
+ Inserted {
+ at: 11,
+ new_content: "ew".into()
+ },
+ Inserted {
+ at: 23,
+ new_content: "!".into()
+ }
+ ],
+ );
+ }
+
+ #[test]
+ fn test_apply() {
+ let old = "This is an old string...";
+ let new = "This is a new text...!";
+
+ let changes = ContentChange::diff(old.as_bytes(), new.as_bytes());
+
+ let mut buffer = old.as_bytes().to_vec();
+ for change in changes {
+ change.apply(&mut buffer);
+ }
+
+ assert_eq!(&buffer, new.as_bytes());
+ }
+}
diff --git a/src/history.rs b/src/history.rs
index 13b2838..a032eaa 100644
--- a/src/history.rs
+++ b/src/history.rs
@@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
use anyhow::{Context, Result};
-use crate::text_diff::TextChange;
+use crate::diff::ContentChange;
#[derive(Serialize, Deserialize, Debug)]
pub struct FileHistory {
@@ -39,15 +39,15 @@ impl FileHistory {
}
}
- pub fn get_content(&self) -> String {
- let mut buffer = String::new();
+ pub fn get_content(&self) -> Vec<u8> {
+ let mut buffer = Vec::new();
for file_change in self.changes.iter().take(self.cursor) {
if let FileChange::Updated(ref updated) = file_change {
for change in updated.iter() {
change.apply(&mut buffer)
}
} else {
- buffer = String::new();
+ buffer.drain(0..);
}
}
buffer
@@ -89,6 +89,6 @@ impl Default for FileHistory {
#[derive(Serialize, Deserialize, Debug)]
pub enum FileChange {
- Updated(Vec<TextChange>),
+ Updated(Vec<ContentChange>),
Deleted,
}
diff --git a/src/lib.rs b/src/lib.rs
index 467ffe7..152219e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,5 @@
pub mod actions;
mod history;
-mod text_diff;
+mod diff;
mod files;
diff --git a/src/text_diff.rs b/src/text_diff.rs
deleted file mode 100644
index bab8799..0000000
--- a/src/text_diff.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-use difference::{Changeset, Difference};
-use serde::{Deserialize, Serialize};
-
-#[derive(Serialize, Deserialize, Debug)]
-pub enum TextChange {
- Inserted { at: usize, new_content: String },
- Deleted { at: usize, upto: usize },
-}
-
-impl TextChange {
- pub fn diff(old: &str, new: &str) -> Vec<Self> {
- let change_set = Changeset::new(old, new, "");
-
- let mut changes = Vec::new();
- let mut at: usize = 0;
-
- for diff in change_set.diffs.iter() {
- match diff {
- Difference::Add(new_content) => {
- let change = TextChange::Inserted {
- at,
- new_content: new_content.to_owned(),
- };
- at += new_content.len();
- changes.push(change);
- }
- Difference::Rem(removed_content) => {
- changes.push(TextChange::Deleted {
- at,
- upto: removed_content.len(),
- });
- }
- Difference::Same(same_content) => {
- at += same_content.len();
- }
- }
- }
-
- changes
- }
-
- pub fn apply(&self, buffer: &mut String) {
- match self {
- TextChange::Deleted { at, upto } => {
- buffer.replace_range(at..upto, "");
- }
- TextChange::Inserted { at, new_content } => {
- buffer.insert_str(*at, new_content);
- }
- }
- }
-}