From 8d73e676158d7cc7fee541db0a431080685758f5 Mon Sep 17 00:00:00 2001 From: Michael Sippel Date: Sun, 24 Jan 2021 23:49:13 +0100 Subject: [PATCH] flatten() for SequenceViews --- src/core/port.rs | 15 ++- src/main.rs | 140 ++++++++++++-------- src/sequence/flatten.rs | 255 +++++++++++++++++++++++++++++++++++++ src/sequence/mod.rs | 1 + src/sequence/vec_buffer.rs | 6 + 5 files changed, 361 insertions(+), 56 deletions(-) create mode 100644 src/sequence/flatten.rs diff --git a/src/core/port.rs b/src/core/port.rs index 5e4b428..dac9cc6 100644 --- a/src/core/port.rs +++ b/src/core/port.rs @@ -71,10 +71,7 @@ impl Clone for ViewPort { //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> -#[derive(Clone)] pub struct InnerViewPort(ViewPort); - -#[derive(Clone)] pub struct OuterViewPort(ViewPort); //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> @@ -98,6 +95,12 @@ impl InnerViewPort { } } +impl Clone for InnerViewPort { + fn clone(&self) -> Self { + InnerViewPort(self.0.clone()) + } +} + //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> impl OuterViewPort { @@ -127,6 +130,12 @@ impl OuterViewPort { } } +impl Clone for OuterViewPort { + fn clone(&self) -> Self { + OuterViewPort(self.0.clone()) + } +} + /* impl OuterViewPort where V::Msg: Clone { diff --git a/src/main.rs b/src/main.rs index 700a3b6..0a67974 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,7 +20,7 @@ use { cgmath::{Vector2, Point2}, termion::event::{Event, Key}, crate::{ - core::{View, Observer, ObserverExt, ObserverBroadcast, ViewPort}, + core::{View, Observer, ObserverExt, ObserverBroadcast, ViewPort, OuterViewPort}, index::{ImplIndexView}, terminal::{ TerminalView, @@ -30,6 +30,7 @@ use { Terminal, TerminalCompositor }, + sequence::{VecBuffer, SequenceView}, grid::{GridOffset, GridWindowIterator}, singleton::{SingletonView, SingletonBuffer}, string_editor::{StringEditor, insert_view::StringInsertView}, @@ -59,71 +60,104 @@ async fn main() { let window_size_port = ViewPort::new(); let mut window_size = SingletonBuffer::new(Vector2::new(0, 0), window_size_port.inner()); + let opening_port = ViewPort::new(); + let mut opening = VecBuffer::new(opening_port.inner()); + + let delim_port = ViewPort::new(); + let mut delim = VecBuffer::new(delim_port.inner()); + + let closing_port = ViewPort::new(); + let mut closing = VecBuffer::new(closing_port.inner()); + + let e1_port = ViewPort::new(); + let mut e1 = VecBuffer::new(e1_port.inner()); + + let e2_port = ViewPort::new(); + let mut e2 = VecBuffer::new(e2_port.inner()); + + opening.push(TerminalAtom::new('[', TerminalStyle::fg_color((180, 120, 80)))); + delim.push(TerminalAtom::new(',', TerminalStyle::fg_color((180, 120, 80)))); + delim.push(TerminalAtom::new(' ', TerminalStyle::fg_color((180, 120, 80)))); + closing.push(TerminalAtom::new(']', TerminalStyle::fg_color((180, 120, 80)))); + + let str_list_port = ViewPort::new(); + let mut str_list = VecBuffer::>>::new(str_list_port.inner()); + + str_list.push(opening_port.outer().to_sequence()); + str_list.push(closing_port.outer().to_sequence()); + + compositor.push( + str_list_port.outer() + .to_sequence() + .flatten() + .to_index() + .map_key( + |idx| Point2::new(*idx as i16, 0 as i16), + |pt| if pt.y == 0 { Some(pt.x as usize) } else { None } + ) + ); + //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> - // cells + // welcome message + task::sleep(std::time::Duration::from_millis(500)).await; + str_list.insert(1, e1_port.outer().to_sequence()); + for c in "Welcome!".chars() { + e1.push(TerminalAtom::new(c, TerminalStyle::fg_color((180, 180, 255)))); + task::sleep(std::time::Duration::from_millis(80)).await; + } + task::sleep(std::time::Duration::from_millis(500)).await; + str_list.insert(2, delim_port.outer().to_sequence()); + str_list.insert(3, e2_port.outer().to_sequence()); + task::sleep(std::time::Duration::from_millis(80)).await; + for c in "This is a flattened SequenceView.".chars() { + e2.push(TerminalAtom::new(c, TerminalStyle::fg_color((180, 180, 255)))); + task::sleep(std::time::Duration::from_millis(80)).await; + } - let cells_port = ViewPort::new(); - let cells = cell_layout::CellLayout::with_port(cells_port.inner()); + task::sleep(std::time::Duration::from_millis(500)).await; - compositor.push(cells_port.outer()); + let l2_port = ViewPort::new(); + let mut l2 = VecBuffer::new(l2_port.inner()); + + *str_list.get_mut(1) = l2_port.outer().to_sequence().flatten(); + + l2.push(opening_port.outer().to_sequence()); + + e1.clear(); + l2.push(e1_port.outer().to_sequence()); + l2.push(closing_port.outer().to_sequence()); + + for c in "they can even be NeStEd!".chars() { + e1.push(TerminalAtom::new(c, TerminalStyle::fg_color((180, 180, 255)))); + task::sleep(std::time::Duration::from_millis(80)).await; + } + + for i in 0 .. 10 { + task::sleep(std::time::Duration::from_millis(100)).await; + + let col = (100+10*i, 55+20*i, 20+ 20*i); + *opening.get_mut(0) = TerminalAtom::new('{', TerminalStyle::fg_color(col)); + *closing.get_mut(0) = TerminalAtom::new('}', TerminalStyle::fg_color(col)); + } + + for i in 0 .. 10 { + task::sleep(std::time::Duration::from_millis(100)).await; + + let col = (100+10*i, 55+20*i, 20+ 20*i); + *opening.get_mut(0) = TerminalAtom::new('<', TerminalStyle::fg_color(col)); + *closing.get_mut(0) = TerminalAtom::new('>', TerminalStyle::fg_color(col)); + } //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> // string editor 1 let mut editor = StringEditor::new(); let (leveled_edit_view, leveled_edit_view_port) = LeveledTermView::new(editor.insert_view()); - cell_layout::CellLayout::set_cell( - &cells, - Point2::new(0, 0), - leveled_edit_view_port - .map_item( - move |_pos, atom| atom.add_style_back( - TerminalStyle::fg_color((200,200,200))))); - //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> // string editor 2 let mut editor2 = StringEditor::new(); let (leveled_edit2_view, leveled_edit2_view_port) = LeveledTermView::new(editor2.insert_view()); - cell_layout::CellLayout::set_cell( - &cells, - Point2::new(1, 1), - leveled_edit2_view_port - .map_item( - move |_pos, atom| atom.add_style_back( - TerminalStyle::fg_color((200,200,200))))); - - //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> - // another view of the string, without editor - cell_layout::CellLayout::set_cell( - &cells, - Point2::new(1, 0), - editor.get_data_port() - .to_sequence() - .to_index() - .map_key( - |idx| Point2::new(*idx as i16, 2 + *idx as i16), - |pt| if pt.x == pt.y-2 { Some(pt.x as usize) } else { None } - ).map_item( - |_key, c| TerminalAtom::new(*c, TerminalStyle::fg_color((80, 20, 180)).add(TerminalStyle::bg_color((40,10,90)))) - ) - ); - - //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> - // welcome message - - for c in "Welcome!".chars() { - editor.insert(c); - task::sleep(std::time::Duration::from_millis(80)).await; - } - - task::sleep(std::time::Duration::from_millis(500)).await; - - for c in "Use arrow keys to navigate.".chars() { - editor2.insert(c); - task::sleep(std::time::Duration::from_millis(80)).await; - } - /*\ <<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> @@ -131,7 +165,7 @@ async fn main() { <<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> \*/ - let mut sel = 0; + let mut sel = 0 as usize; leveled_edit_view.write().unwrap().set_level(if sel == 0 {1} else {0}); leveled_edit2_view.write().unwrap().set_level(if sel == 1 {1} else {0}); diff --git a/src/sequence/flatten.rs b/src/sequence/flatten.rs new file mode 100644 index 0000000..b2a711e --- /dev/null +++ b/src/sequence/flatten.rs @@ -0,0 +1,255 @@ +use { + async_std::stream::StreamExt, + std::{ + sync::{Arc, Weak, RwLock}, + collections::{HashMap, HashSet} + }, + crate::{ + core::{ + View, Observer, ObserverExt, ObserverBroadcast, + ViewPort, InnerViewPort, OuterViewPort, + channel::{ChannelSender, ChannelReceiver} + }, + sequence::SequenceView + } +}; + +impl OuterViewPort +where V1: SequenceView> + ?Sized + 'static, + V2: SequenceView + ?Sized + 'static +{ + pub fn flatten(&self) -> OuterViewPort> { + let port = ViewPort::new(); + Flatten::new(self.clone(), port.inner()); + port.into_outer() + } +} + +pub struct Flatten +where V1: SequenceView> + ?Sized + 'static, + V2: SequenceView + ?Sized + 'static +{ + length: usize, + top: Arc>>, + chunks: HashMap>>>, + + cast: Arc>>> +} + +struct TopObserver +where V1: SequenceView> + ?Sized + 'static, + V2: SequenceView + ?Sized + 'static +{ + view: Option>, + sender: ChannelSender> +} + +struct BotObserver +where V2: SequenceView + ?Sized + 'static +{ + offset: usize, + view: Option>, + sender: ChannelSender> +} + +impl Observer for TopObserver +where V1: SequenceView> + ?Sized + 'static, + V2: SequenceView + ?Sized + 'static +{ + fn reset(&mut self, view: Option>) { + let old_len = self.view.len().unwrap_or(0); + self.view = view; + let new_len = self.view.len().unwrap_or(0); + + self.notify_each(0 .. std::cmp::max(old_len, new_len)); + } + + fn notify(&self, chunk_idx: &usize) { + self.sender.send(*chunk_idx); + } +} + +impl Observer for BotObserver +where V2: SequenceView + ?Sized + 'static +{ + fn reset(&mut self, src: Option>) { + let old_len = self.view.len().unwrap_or(0); + self.view = src; + let new_len = self.view.len().unwrap_or(0); + + self.notify_each(0 .. std::cmp::max(old_len, new_len)); + } + + fn notify(&self, idx: &usize) { + self.sender.send(*idx); + } +} + +impl View for Flatten +where V1: SequenceView> + ?Sized + 'static, + V2: SequenceView + ?Sized + 'static +{ + type Msg = usize; +} + +impl SequenceView for Flatten +where V1: SequenceView> + ?Sized + 'static, + V2: SequenceView + ?Sized + 'static +{ + type Item = V2::Item; + + fn get(&self, idx: &usize) -> Option { + let chunk = self.chunks[&self.get_chunk_idx(*idx)?].read().unwrap(); + chunk.view.get(&(*idx - chunk.offset)) + } + + fn len(&self) -> Option { + Some(self.length) + } +} + +impl Flatten +where V1: SequenceView> + ?Sized + 'static, + V2: SequenceView + ?Sized + 'static +{ + pub fn new( + top_port: OuterViewPort, + out_port: InnerViewPort> + ) -> Arc> { + let (sender, mut recv) = crate::core::channel::set_channel(); + + let top_obs = Arc::new(RwLock::new( + TopObserver { + view: None, + sender + } + )); + + let flat = Arc::new(RwLock::new(Flatten:: { + length: 0, + top: top_obs.clone(), + chunks: HashMap::new(), + cast: out_port.get_broadcast() + })); + + let f = flat.clone(); + let cast = out_port.get_broadcast(); + async_std::task::spawn(async move { + while let Some(chunk_idx) = recv.next().await { + if let Some(mut chunk_rcv) = f.write().unwrap().update_chunk(chunk_idx) { + let f = f.clone(); + let cast = cast.clone(); + async_std::task::spawn(async move { + while let Some(idx) = chunk_rcv.next().await { + let mut flat = f.write().unwrap(); + + let chunk = flat.chunks[&chunk_idx].read().unwrap(); + let chunk_offset = chunk.offset; + let chunk_len = chunk.view.len().unwrap_or(0); + drop(chunk); + + let mut dirty_idx = Vec::new(); + if idx+1 >= chunk_len { + dirty_idx = flat.update_offsets(chunk_idx); + } + + drop(flat); + + cast.notify(&(idx + chunk_offset)); + cast.notify_each(dirty_idx); + } + }); + } + } + }); + + top_port.add_observer(top_obs); + + out_port.set_view(Some(flat.clone())); + flat + } + + /// the top-sequence has changed the item at chunk_idx, + /// create a new observer for the contained sub sequence + fn update_chunk(&mut self, chunk_idx: usize) -> Option>> { + if let Some(chunk_port) = self.top.read().unwrap().view.get(&chunk_idx) { + let (sender, recv) = crate::core::channel::set_channel(); + let chunk_obs = Arc::new(RwLock::new( + BotObserver { + offset: + if chunk_idx > 0 { + if let Some(prev_chunk) = self.chunks.get(&(chunk_idx-1)) { + let prev_chunk = prev_chunk.read().unwrap(); + prev_chunk.offset + prev_chunk.view.len().unwrap_or(0) + } else { + 0 + } + } else { + 0 + }, + view: None, + sender + } + )); + + self.chunks.insert(chunk_idx, chunk_obs.clone()); + chunk_port.add_observer(chunk_obs); + + Some(recv) + } else { + self.chunks.remove(&chunk_idx); + None + } + } + + /// recalculate all chunk offsets beginning at start_idx + /// and update length of flattened sequence + fn update_offsets(&mut self, start_idx: usize) -> Vec { + let top_len = self.top.read().unwrap().view.len().unwrap_or(0); + + let first_chunk = self.chunks.get(&start_idx).unwrap().read().unwrap(); + let mut start_offset = first_chunk.offset + first_chunk.view.len().unwrap_or(0); + let mut cur_offset = start_offset; + + let mut dirty_idx = Vec::new(); + + for chunk_idx in start_idx+1 .. top_len { + if let Some(cur_chunk) = self.chunks.get(&chunk_idx) { + let mut cur_chunk = cur_chunk.write().unwrap(); + + let chunk_len = cur_chunk.view.len().unwrap_or(0); + let old_offset = cur_chunk.offset; + cur_chunk.offset = cur_offset; + + if old_offset != cur_offset { + dirty_idx.extend( + std::cmp::min(old_offset, cur_offset) + .. std::cmp::max(old_offset, cur_offset) + chunk_len + ); + } + cur_offset += chunk_len; + } + } + + let old_length = self.length; + self.length = cur_offset; + + dirty_idx.extend(self.length .. old_length); + dirty_idx + } + + /// given an index in the flattened sequence, + /// which sub-sequence does it belong to? + fn get_chunk_idx(&self, glob_idx: usize) -> Option { + for chunk_idx in 0 .. self.top.read().unwrap().view.len().unwrap_or(0) { + if let Some(cur_chunk) = self.chunks.get(&chunk_idx) { + let cur_chunk = cur_chunk.read().unwrap(); + if glob_idx < cur_chunk.offset + cur_chunk.view.len().unwrap_or(0) { + return Some(chunk_idx) + } + } + } + None + } +} + diff --git a/src/sequence/mod.rs b/src/sequence/mod.rs index bb09a04..f10a1ea 100644 --- a/src/sequence/mod.rs +++ b/src/sequence/mod.rs @@ -1,6 +1,7 @@ pub mod seq2idx; pub mod vec_buffer; +pub mod flatten; pub use { seq2idx::{Sequence2Index}, diff --git a/src/sequence/vec_buffer.rs b/src/sequence/vec_buffer.rs index b465454..23a40fe 100644 --- a/src/sequence/vec_buffer.rs +++ b/src/sequence/vec_buffer.rs @@ -166,6 +166,12 @@ where T: Clone + Send + Sync + 'static { self.cast.notify(&diff); } + pub fn clear(&mut self) { + for _ in 0 .. self.len() { + self.remove(0); + } + } + pub fn len(&self) -> usize { self.data.read().unwrap().len() }