mirror of
				https://github.com/AppFlowy-IO/AppFlowy.git
				synced 2025-10-31 18:15:09 +00:00 
			
		
		
		
	extend cursor with generic metric
This commit is contained in:
		
							parent
							
								
									1ec4655d1b
								
							
						
					
					
						commit
						bc7da582a3
					
				| @ -47,6 +47,8 @@ impl Document { | ||||
|     pub fn format(&mut self, interval: Interval, attribute: Attribute) -> Result<(), OTError> { | ||||
|         log::debug!("format with {} at {}", attribute, interval); | ||||
| 
 | ||||
|         // let format_delta = self.view.format(&self.delta, attribute, interval)?;
 | ||||
| 
 | ||||
|         self.update_with_attribute(attribute, interval) | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -1,13 +1,43 @@ | ||||
| use crate::{ | ||||
|     client::view::FormatExt, | ||||
|     core::{Attribute, Delta, Interval}, | ||||
|     core::{Attribute, AttributeScope, Attributes, Delta, DeltaBuilder, DeltaIter, Interval}, | ||||
| }; | ||||
| 
 | ||||
| pub struct FormatLinkAtCaretPositionExt {} | ||||
| 
 | ||||
| impl FormatExt for FormatLinkAtCaretPositionExt { | ||||
|     fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> { | ||||
|         unimplemented!() | ||||
|         let mut iter = DeltaIter::new(delta); | ||||
|         iter.seek(interval.start); | ||||
|         let (before, after) = (iter.next(), iter.next()); | ||||
|         let mut start = interval.start; | ||||
|         let mut retain = 0; | ||||
| 
 | ||||
|         if let Some(before) = before { | ||||
|             if before.contain_attribute(attribute) { | ||||
|                 start -= before.length(); | ||||
|                 retain += before.length(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if let Some(after) = after { | ||||
|             if after.contain_attribute(attribute) { | ||||
|                 if retain != 0 { | ||||
|                     retain += after.length(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if retain == 0 { | ||||
|             return None; | ||||
|         } | ||||
| 
 | ||||
|         Some( | ||||
|             DeltaBuilder::new() | ||||
|                 .retain(start, Attributes::default()) | ||||
|                 .retain(retain, (attribute.clone()).into()) | ||||
|                 .build(), | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -15,7 +45,17 @@ pub struct ResolveLineFormatExt {} | ||||
| 
 | ||||
| impl FormatExt for ResolveLineFormatExt { | ||||
|     fn apply(&self, delta: &Delta, interval: Interval, attribute: &Attribute) -> Option<Delta> { | ||||
|         unimplemented!() | ||||
|         if attribute.scope != AttributeScope::Block { | ||||
|             return None; | ||||
|         } | ||||
| 
 | ||||
|         let mut new_delta = Delta::new(); | ||||
|         new_delta.retain(interval.start, Attributes::default()); | ||||
| 
 | ||||
|         let mut iter = DeltaIter::new(delta); | ||||
|         iter.seek(interval.start); | ||||
| 
 | ||||
|         None | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -44,7 +44,7 @@ impl InsertExt for ResetLineFormatOnNewLineExt { | ||||
|         } | ||||
| 
 | ||||
|         let mut iter = DeltaIter::new(delta); | ||||
|         iter.seek_to(index); | ||||
|         iter.seek(index); | ||||
|         let maybe_next_op = iter.next(); | ||||
|         if maybe_next_op.is_none() { | ||||
|             return None; | ||||
|  | ||||
| @ -71,7 +71,7 @@ pub enum AttributeKey { | ||||
|     UnChecked, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| #[derive(Debug, PartialEq, Eq, Clone)] | ||||
| pub enum AttributeScope { | ||||
|     Inline, | ||||
|     Block, | ||||
| @ -79,7 +79,7 @@ pub enum AttributeScope { | ||||
|     Ignore, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct Attribute { | ||||
|     pub key: AttributeKey, | ||||
|     pub value: String, | ||||
| @ -93,6 +93,14 @@ impl fmt::Display for Attribute { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl std::convert::Into<Attributes> for Attribute { | ||||
|     fn into(self) -> Attributes { | ||||
|         let mut attributes = Attributes::new(); | ||||
|         attributes.add(self); | ||||
|         attributes | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct AttrsBuilder { | ||||
|     inner: Attributes, | ||||
| } | ||||
|  | ||||
| @ -8,8 +8,9 @@ pub struct Cursor<'a> { | ||||
|     delta: &'a Delta, | ||||
|     interval: Interval, | ||||
|     iterator: Iter<'a, Operation>, | ||||
|     offset: usize, | ||||
|     offset_op: Option<&'a Operation>, | ||||
|     char_index: usize, | ||||
|     op_index: usize, | ||||
|     current_op: Option<&'a Operation>, | ||||
| } | ||||
| 
 | ||||
| impl<'a> Cursor<'a> { | ||||
| @ -18,29 +19,31 @@ impl<'a> Cursor<'a> { | ||||
|             delta, | ||||
|             interval, | ||||
|             iterator: delta.ops.iter(), | ||||
|             offset: 0, | ||||
|             offset_op: None, | ||||
|             char_index: 0, | ||||
|             op_index: 0, | ||||
|             current_op: None, | ||||
|         }; | ||||
|         cursor | ||||
|     } | ||||
| 
 | ||||
|     pub fn next_op(&mut self) -> Option<Operation> { | ||||
|         let mut next_op = self.offset_op.take(); | ||||
| 
 | ||||
|         let mut next_op = self.current_op.take(); | ||||
|         if next_op.is_none() { | ||||
|             next_op = self.iterator.next(); | ||||
|         } | ||||
| 
 | ||||
|         let mut find_op = None; | ||||
|         while find_op.is_none() && next_op.is_some() { | ||||
|             self.op_index += 1; | ||||
| 
 | ||||
|             let op = next_op.unwrap(); | ||||
|             if self.offset < self.interval.start { | ||||
|                 let intersect = | ||||
|                     Interval::new(self.offset, self.offset + op.length()).intersect(self.interval); | ||||
|             if self.char_index < self.interval.start { | ||||
|                 let intersect = Interval::new(self.char_index, self.char_index + op.length()) | ||||
|                     .intersect(self.interval); | ||||
|                 if intersect.is_empty() { | ||||
|                     self.offset += op.length(); | ||||
|                     self.char_index += op.length(); | ||||
|                 } else { | ||||
|                     if let Some(new_op) = op.shrink(intersect.translate_neg(self.offset)) { | ||||
|                     if let Some(new_op) = op.shrink(intersect.translate_neg(self.char_index)) { | ||||
|                         // shrink the op to fit the intersect range
 | ||||
|                         // ┌──────────────┐
 | ||||
|                         // │ 1 2 3 4 5 6  │
 | ||||
| @ -50,11 +53,11 @@ impl<'a> Cursor<'a> { | ||||
|                         // op = "45"
 | ||||
|                         find_op = Some(new_op); | ||||
|                     } | ||||
|                     self.offset = intersect.end; | ||||
|                     self.char_index = intersect.end; | ||||
|                 } | ||||
|             } else { | ||||
|                 // the interval passed in the shrink function is base on the op not the delta.
 | ||||
|                 if let Some(new_op) = op.shrink(self.interval.translate_neg(self.offset)) { | ||||
|                 if let Some(new_op) = op.shrink(self.interval.translate_neg(self.char_index)) { | ||||
|                     find_op = Some(new_op); | ||||
|                 } | ||||
|                 // for example: extract the ops from three insert ops with interval [2,5). the
 | ||||
| @ -67,43 +70,96 @@ impl<'a> Cursor<'a> { | ||||
|                 // └──────┘  └─▲────┘  └───▲──┘
 | ||||
|                 //             │  [2, 5)   │
 | ||||
|                 //
 | ||||
|                 self.offset += min(self.interval.size(), op.length()); | ||||
|                 self.char_index += min(self.interval.size(), op.length()); | ||||
|             } | ||||
| 
 | ||||
|             match find_op { | ||||
|                 None => next_op = self.iterator.next(), | ||||
|                 Some(_) => self.interval.start = self.offset, | ||||
|                 Some(_) => self.interval.start = self.char_index, | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         find_op | ||||
|     } | ||||
| 
 | ||||
|     pub fn seek_to(&mut self, index: usize) -> Result<(), OTError> { | ||||
|         if self.offset > index { | ||||
|             let msg = format!( | ||||
|                 "{} should be greater than current offset: {}", | ||||
|                 index, self.offset | ||||
|             ); | ||||
|             return Err(ErrorBuilder::new(OTErrorCode::IncompatibleLength) | ||||
|                 .msg(&msg) | ||||
|                 .build()); | ||||
|         } | ||||
|     pub fn seek<M: Metric>(&mut self, index: usize) -> Result<(), OTError> { | ||||
|         self.current_op = M::seek(self, index)?; | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|         let mut offset = 0; | ||||
|         while let Some(op) = self.iterator.next() { | ||||
|             if offset != 0 { | ||||
|                 self.offset = offset; | ||||
| pub trait Metric { | ||||
|     fn seek<'a, 'b>( | ||||
|         cursor: &'b mut Cursor<'a>, | ||||
|         index: usize, | ||||
|     ) -> Result<Option<&'a Operation>, OTError>; | ||||
| } | ||||
| 
 | ||||
| pub struct OpMetric {} | ||||
| 
 | ||||
| impl Metric for OpMetric { | ||||
|     fn seek<'a, 'b>( | ||||
|         cursor: &'b mut Cursor<'a>, | ||||
|         index: usize, | ||||
|     ) -> Result<Option<&'a Operation>, OTError> { | ||||
|         let _ = check_bound(cursor.op_index, index)?; | ||||
| 
 | ||||
|         let mut offset = cursor.op_index; | ||||
|         let mut op_at_index = None; | ||||
| 
 | ||||
|         while let Some(op) = cursor.iterator.next() { | ||||
|             if offset != cursor.op_index { | ||||
|                 cursor.char_index += op.length(); | ||||
|                 cursor.op_index = offset; | ||||
|             } | ||||
| 
 | ||||
|             offset += op.length(); | ||||
|             self.offset_op = Some(op); | ||||
|             offset += 1; | ||||
|             op_at_index = Some(op); | ||||
| 
 | ||||
|             if offset >= index { | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         Ok(()) | ||||
|         Ok(op_at_index) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct CharMetric {} | ||||
| 
 | ||||
| impl Metric for CharMetric { | ||||
|     fn seek<'a, 'b>( | ||||
|         cursor: &'b mut Cursor<'a>, | ||||
|         index: usize, | ||||
|     ) -> Result<Option<&'a Operation>, OTError> { | ||||
|         let _ = check_bound(cursor.char_index, index)?; | ||||
| 
 | ||||
|         let mut offset = cursor.char_index; | ||||
|         let mut op_at_index = None; | ||||
|         while let Some(op) = cursor.iterator.next() { | ||||
|             if offset != cursor.char_index { | ||||
|                 cursor.char_index = offset; | ||||
|                 cursor.op_index += 1; | ||||
|             } | ||||
| 
 | ||||
|             offset += op.length(); | ||||
|             op_at_index = Some(op); | ||||
| 
 | ||||
|             if offset >= index { | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         Ok(op_at_index) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn check_bound(current: usize, target: usize) -> Result<(), OTError> { | ||||
|     if current > target { | ||||
|         let msg = format!("{} should be greater than current: {}", target, current); | ||||
|         return Err(ErrorBuilder::new(OTErrorCode::IncompatibleLength) | ||||
|             .msg(&msg) | ||||
|             .build()); | ||||
|     } | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| @ -23,8 +23,8 @@ impl<'a> DeltaIter<'a> { | ||||
| 
 | ||||
|     pub fn ops(&mut self) -> Vec<Operation> { self.collect::<Vec<_>>() } | ||||
| 
 | ||||
|     pub fn seek_to(&mut self, n_char: usize) -> Result<(), OTError> { | ||||
|         let _ = self.cursor.seek_to(n_char)?; | ||||
|     pub fn seek(&mut self, n_char: usize) -> Result<(), OTError> { | ||||
|         let _ = self.cursor.seek::<CharMetric>(n_char)?; | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
| @ -102,7 +102,7 @@ impl<'a> Iterator for AttributesIter<'a> { | ||||
| 
 | ||||
| pub(crate) fn attributes_at_index(delta: &Delta, index: usize) -> Attributes { | ||||
|     let mut iter = AttributesIter::new(delta); | ||||
|     iter.seek_to(index); | ||||
|     iter.seek(index); | ||||
|     match iter.next() { | ||||
|         // None => Attributes::Follow,
 | ||||
|         None => Attributes::new(), | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| use crate::core::{Attributes, Builder, Interval}; | ||||
| use crate::core::{Attribute, Attributes, Builder, Interval}; | ||||
| use bytecount::num_chars; | ||||
| use serde::__private::Formatter; | ||||
| use std::{ | ||||
| @ -48,6 +48,10 @@ impl Operation { | ||||
| 
 | ||||
|     pub fn has_attribute(&self) -> bool { !self.get_attributes().is_empty() } | ||||
| 
 | ||||
|     pub fn contain_attribute(&self, attribute: &Attribute) -> bool { | ||||
|         self.get_attributes().contains_key(&attribute.key) | ||||
|     } | ||||
| 
 | ||||
|     pub fn length(&self) -> usize { | ||||
|         match self { | ||||
|             Operation::Delete(n) => *n, | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 appflowy
						appflowy