diff --git a/Cargo.lock b/Cargo.lock index bfdb62936e..7647e3ccd3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4910,6 +4910,7 @@ dependencies = [ "parking_lot 0.11.2", "postage", "rand 0.8.5", + "regex", "smallvec", "sum_tree", "util", diff --git a/crates/text/Cargo.toml b/crates/text/Cargo.toml index cbbd65027e..4fc09eff46 100644 --- a/crates/text/Cargo.toml +++ b/crates/text/Cargo.toml @@ -23,6 +23,7 @@ log = { version = "0.4.16", features = ["kv_unstable_serde"] } parking_lot = "0.11" postage = { version = "0.4.1", features = ["futures-traits"] } rand = { version = "0.8.3", optional = true } +regex = "1.5" smallvec = { version = "1.6", features = ["union"] } [dev-dependencies] diff --git a/crates/text/src/tests.rs b/crates/text/src/tests.rs index 2d57b65792..c0cc0556d5 100644 --- a/crates/text/src/tests.rs +++ b/crates/text/src/tests.rs @@ -43,7 +43,7 @@ fn test_random_edits(mut rng: StdRng) { .take(reference_string_len) .collect::(); let mut buffer = Buffer::new(0, 0, reference_string.clone().into()); - reference_string = reference_string.replace("\r", ""); + LineEnding::strip_carriage_returns(&mut reference_string); buffer.history.group_interval = Duration::from_millis(rng.gen_range(0..=200)); let mut buffer_versions = Vec::new(); @@ -58,7 +58,6 @@ fn test_random_edits(mut rng: StdRng) { for (old_range, new_text) in edits.iter().rev() { reference_string.replace_range(old_range.clone(), &new_text); } - reference_string = reference_string.replace("\r", ""); assert_eq!(buffer.text(), reference_string); log::info!( @@ -165,14 +164,14 @@ fn test_line_endings() { LineEnding::Windows ); - let mut buffer = Buffer::new(0, 0, "one\r\ntwo".into()); - assert_eq!(buffer.text(), "one\ntwo"); + let mut buffer = Buffer::new(0, 0, "one\r\ntwo\rthree".into()); + assert_eq!(buffer.text(), "one\ntwo\nthree"); assert_eq!(buffer.line_ending(), LineEnding::Windows); buffer.check_invariants(); - buffer.edit([(buffer.len()..buffer.len(), "\r\nthree")]); + buffer.edit([(buffer.len()..buffer.len(), "\r\nfour")]); buffer.edit([(0..0, "zero\r\n")]); - assert_eq!(buffer.text(), "zero\none\ntwo\nthree"); + assert_eq!(buffer.text(), "zero\none\ntwo\nthree\nfour"); assert_eq!(buffer.line_ending(), LineEnding::Windows); buffer.check_invariants(); } diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index 4d1e50b274..f73163438b 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -18,6 +18,7 @@ pub use anchor::*; use anyhow::Result; use clock::ReplicaId; use collections::{HashMap, HashSet}; +use lazy_static::lazy_static; use locator::Locator; use operation_queue::OperationQueue; pub use patch::Patch; @@ -26,10 +27,12 @@ pub use point_utf16::*; use postage::{barrier, oneshot, prelude::*}; #[cfg(any(test, feature = "test-support"))] pub use random_char_iter::*; +use regex::Regex; use rope::TextDimension; pub use rope::{Chunks, Rope, TextSummary}; pub use selection::*; use std::{ + borrow::Cow, cmp::{self, Ordering}, future::Future, iter::Iterator, @@ -42,6 +45,10 @@ pub use subscription::*; pub use sum_tree::Bias; use sum_tree::{FilterCursor, SumTree}; +lazy_static! { + static ref CARRIAGE_RETURNS_REGEX: Regex = Regex::new("\r\n|\r").unwrap(); +} + pub type TransactionId = clock::Local; pub struct Buffer { @@ -1472,6 +1479,15 @@ impl Buffer { log::info!("mutating buffer {} with {:?}", self.replica_id, edits); let op = self.edit(edits.iter().cloned()); + if let Operation::Edit(edit) = &op { + assert_eq!(edits.len(), edit.new_text.len()); + for (edit, new_text) in edits.iter_mut().zip(&edit.new_text) { + edit.1 = new_text.clone(); + } + } else { + unreachable!() + } + (edits, op) } @@ -2370,12 +2386,14 @@ impl LineEnding { } pub fn strip_carriage_returns(text: &mut String) { - text.retain(|c| c != '\r') + if let Cow::Owned(replaced) = CARRIAGE_RETURNS_REGEX.replace_all(text, "\n") { + *text = replaced; + } } fn strip_carriage_returns_from_arc(text: Arc) -> Arc { - if text.contains('\r') { - text.replace('\r', "").into() + if let Cow::Owned(replaced) = CARRIAGE_RETURNS_REGEX.replace_all(&text, "\n") { + replaced.into() } else { text }