diff --git a/ext/yrb/src/lib.rs b/ext/yrb/src/lib.rs index e7d59d42..a84dc229 100644 --- a/ext/yrb/src/lib.rs +++ b/ext/yrb/src/lib.rs @@ -488,6 +488,9 @@ fn init() -> Result<(), Error> { method!(YXmlText::yxml_text_attributes, 1), ) .expect("cannot define private method: yxml_text_attributes"); + yxml_text + .define_private_method("yxml_text_diff", method!(YXmlText::yxml_text_diff, 1)) + .expect("cannot define private method: yxml_text_diff"); yxml_text .define_private_method("yxml_text_format", method!(YXmlText::yxml_text_format, 4)) .expect("cannot define private method: yxml_text_format"); diff --git a/ext/yrb/src/yxml_text.rs b/ext/yrb/src/yxml_text.rs index 5780b7fc..c21577d3 100644 --- a/ext/yrb/src/yxml_text.rs +++ b/ext/yrb/src/yxml_text.rs @@ -1,9 +1,11 @@ use crate::utils::map_rhash_to_attrs; +use crate::ydiff::YDiff; use crate::yvalue::YValue; use crate::yxml_fragment::YXmlFragment; use crate::{YTransaction, YXmlElement}; -use magnus::{Error, IntoValue, RHash, Value}; +use magnus::{Error, IntoValue, RHash, Value, RArray}; use std::cell::RefCell; +use yrs::types::text::YChange; use yrs::{Any, GetString, Text, Xml, XmlNode, XmlTextRef}; #[magnus::wrap(class = "Y::XMLText")] @@ -13,6 +15,38 @@ pub(crate) struct YXmlText(pub(crate) RefCell); unsafe impl Send for YXmlText {} impl YXmlText { + pub(crate) fn yxml_text_diff(&self, transaction: &YTransaction) -> RArray { + let tx = transaction.transaction(); + let tx = tx.as_ref().unwrap(); + + RArray::from_iter( + self.0 + .borrow() + .diff(tx, YChange::identity) + .iter() + .map(move |diff| { + let yvalue = YValue::from(diff.insert.clone()); + let insert = yvalue.0.into_inner(); + let attributes = diff.attributes.as_ref().map_or_else( + || None, + |boxed_attrs| { + let attributes = RHash::new(); + for (key, value) in boxed_attrs.iter() { + let key = key.to_string(); + let value = YValue::from(value.clone()).0.into_inner(); + attributes.aset(key, value).expect("cannot add value"); + } + Some(attributes) + }, + ); + YDiff { + ydiff_insert: insert, + ydiff_attrs: attributes, + } + .into_value() + }), + ) + } pub(crate) fn yxml_text_attributes(&self, transaction: &YTransaction) -> RHash { let tx = transaction.transaction(); let tx = tx.as_ref().unwrap(); diff --git a/lib/y/xml.rb b/lib/y/xml.rb index b1204c2f..6732081a 100644 --- a/lib/y/xml.rb +++ b/lib/y/xml.rb @@ -526,6 +526,15 @@ def detach(subscription_id) yxml_text_unobserve(subscription_id) end + # Diff + # + # @return[Array] + def diff + document.current_transaction do |tx| + yxml_text_diff(tx) + end + end + # Format text # # @param index [Integer] @@ -791,6 +800,12 @@ def can_insert?(value) # # @return [Hash] + # @!method yxml_text_diff(tx) + # Returns text changes as list of diffs + # + # @param transaction [Y::Transaction] + # @return [Array] + # @!method yxml_text_format(tx, index, length, attrs) # # @param tx [Y::Transaction] diff --git a/spec/y/xml_text_spec.rb b/spec/y/xml_text_spec.rb index 563a3875..78a36bc9 100644 --- a/spec/y/xml_text_spec.rb +++ b/spec/y/xml_text_spec.rb @@ -46,6 +46,21 @@ expect(xml_text.to_s).to eq("Hello, World!") end + it "get a list of changes" do + doc = Y::Doc.new + xml_text = doc.get_xml_text("my text") + + xml_text.insert(0, "Hello, World!", { format: "bold" }) + xml_text.insert(13, " From Hannes.", { format: "italic" }) + + expect(xml_text.diff.map(&:to_h)).to eq([ + { insert: "Hello, World!", + attrs: { "format" => "bold" } }, + { insert: " From Hannes.", + attrs: { "format" => "italic" } } + ]) + end + it "inserts string at position" do doc = Y::Doc.new xml_text = doc.get_xml_text("my xml text")