pub mod file; mod tags; use std::collections::VecDeque; use std::sync::{Arc, Mutex}; use tags::RangeState; pub use tags::{BoundState, Format, TagEntry}; /// Main content buffer of a Novelist file /// /// The content is a simple text buffer of UTF-8 strings. Formatting /// tags are applied independently via the tags abstraction. pub struct Buffer { // TODO: how does mixed RTL and LTR text get handled here? pub content: Mutex, pub tags: Mutex>, } /// A glue trait to connect the storage buffer to a UI toolkit pub trait PopulateBuffer { /// Populate a UI text buffer from a storage buffer fn populate(&self, buffer: &Buffer); } impl Buffer { /// Create a new empty Buffer pub fn new() -> Arc { Arc::new(Self { content: Mutex::new(String::new()), tags: Mutex::new(vec![].into()), }) } /// Pass a UI buffer to populate from this storage buffer pub fn populate_ui(self: &Arc, hook: &impl PopulateBuffer) { hook.populate(self) } /// Toggle a style for a particular range of characters /// /// This function MUST handle specific corner cases around /// overlapping ranges. /// /// Returns a set of styles that MUST be applied to the TextBuffer pub fn toggle_style( self: &Arc, (start, end): (i32, i32), style: Format, ) -> Vec<(i32, i32, Format)> { // 1. Go through linked list and compare list-pos to new-pos // 2. If NoOverlap, continue // 3. If BeforeOverlap, expand start-number // 4. If FullOverlap, ignore (return) // 5. If AfterOverlap, expand end-number // 6. If no match was found: insert && sort // The action we would like to apply let target = TagEntry { start, end, style }; // Lock existing tags and iterate through them let mut tags = self.tags.lock().unwrap(); for entry in tags.iter_mut() { // First perform a simple boundry check match entry.in_range(&target) { RangeState::Under => continue, RangeState::Over => break, RangeState::InRange => {}, } // If full-overlap we invert the desired action // Then handle before and after overlapjjjjjjtncthgki.iidukntxgbeubyx.ixptxddcdpexe } todo!() } }