From afaa9d220b1a18e7ff03e599fbaa5a07689cf567 Mon Sep 17 00:00:00 2001 From: Michael Sippel Date: Mon, 18 Jan 2021 17:00:53 +0100 Subject: [PATCH] refactor string editor --- src/main.rs | 73 ++++---- src/sequence/vec_buffer.rs | 27 ++- src/singleton/buffer.rs | 45 +++-- src/string_editor.rs | 353 +++++++++++++++++++++++++------------ 4 files changed, 319 insertions(+), 179 deletions(-) diff --git a/src/main.rs b/src/main.rs index 076bd31..66d2022 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,7 +28,8 @@ use { TerminalCompositor }, grid::{GridOffset, GridWindowIterator}, - singleton::{SingletonView, SingletonBuffer} + singleton::{SingletonView, SingletonBuffer}, + string_editor::{StringEditor} } }; @@ -52,51 +53,43 @@ async fn main() { \*/ let window_size_port = ViewPort::new(); - let window_size = SingletonBuffer::new(Vector2::new(0, 0), window_size_port.inner()); + let mut window_size = SingletonBuffer::new(Vector2::new(0, 0), window_size_port.inner()); //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> // string editor - let edit_port = ViewPort::::new(); - let mut editor = string_editor::StringEditor::new(edit_port.inner()); + let mut editor = StringEditor::new(); - let edit_offset_port = ViewPort::::new(); - let edit_o = GridOffset::new(edit_offset_port.inner()); - - edit_port.add_observer(edit_o.clone()); - - compositor.push( - edit_offset_port - .into_outer() - // add a nice black background - .map_item(|a| a.add_style_back(TerminalStyle::bg_color((0,0,0)))) - ); - - edit_o.write().unwrap().set_offset(Vector2::new(40, 4)); - - //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> - // Vec-Buffer - let vec_port = ViewPort::new(); - let mut vec_buf = sequence::VecBuffer::::new(vec_port.inner()); - - // project Vec-Buffer - let vec_term_view = vec_port.outer() - .to_sequence() - .to_index() - .map_key( - |idx: &usize| Point2::::new(*idx as i16, 0), - |pt: &Point2| if pt.y == 0 { Some(pt.x as usize) } else { None } - ) - .map_item( - |c| TerminalAtom::new(*c, TerminalStyle::fg_color((200, 10, 10))) + let edit_view_port = ViewPort::new(); + let edit_view = + string_editor::insert_view::StringEditView::new( + editor.get_cursor_port(), + editor.get_data_port().to_sequence(), + edit_view_port.inner() ); - compositor.push(vec_term_view); + compositor.push( + edit_view_port.outer() + .map_item( + |_pos, atom| atom.add_style_back( + TerminalStyle::fg_color((200,200,200)) + .add(TerminalStyle::bg_color((0,0,0))) + .add(TerminalStyle::bold(true))) + ) + ); - vec_buf.push('a'); - vec_buf.push('b'); - vec_buf.push('c'); - vec_buf.insert(1, 'x'); - vec_buf.remove(2); + //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + // another view of the string, without editor + compositor.push( + editor.get_data_port() + .to_sequence() + .to_index() + .map_key( + |idx| Point2::new(*idx as i16, 2), + |pt| if pt.y == 2 { Some(pt.x as usize) } else { None } + ).map_item( + |_key, c| TerminalAtom::new(*c, TerminalStyle::fg_color((0, 200, 0))) + ) + ); /*\ <<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> @@ -105,7 +98,7 @@ async fn main() { \*/ loop { match term.next_event().await { - TerminalEvent::Resize(size) => window_size.write().unwrap().set(size), + TerminalEvent::Resize(size) => window_size.set(size), TerminalEvent::Input(Event::Key(Key::Left)) => editor.prev(), TerminalEvent::Input(Event::Key(Key::Right)) => editor.next(), TerminalEvent::Input(Event::Key(Key::Home)) => editor.goto(0), diff --git a/src/sequence/vec_buffer.rs b/src/sequence/vec_buffer.rs index a1c75e7..70ff10a 100644 --- a/src/sequence/vec_buffer.rs +++ b/src/sequence/vec_buffer.rs @@ -70,19 +70,28 @@ where T: Clone + Send + Sync + 'static { fn notify(&self, diff: &VecDiff) { match diff { VecDiff::Push(_) => { - let mut l = self.cur_len.write().unwrap(); - self.cast.notify(&l); - *l += 1; + let l = { + let mut l = self.cur_len.write().unwrap(); + *l += 1; + *l + }; + self.cast.notify(&(l - 1)); }, VecDiff::Remove(idx) => { - let mut l = self.cur_len.write().unwrap(); - *l -= 1; - self.cast.notify_each(*idx .. *l+1); + let l = { + let mut l = self.cur_len.write().unwrap(); + *l -= 1; + *l + 1 + }; + self.cast.notify_each(*idx .. l); }, VecDiff::Insert{ idx, val: _ } => { - let mut l = self.cur_len.write().unwrap(); - *l += 1; - self.cast.notify_each(*idx .. *l); + let l = { + let mut l = self.cur_len.write().unwrap(); + *l += 1; + *l + }; + self.cast.notify_each(*idx .. l); }, VecDiff::Update{ idx, val: _ } => { self.cast.notify(&idx); diff --git a/src/singleton/buffer.rs b/src/singleton/buffer.rs index 7ce00cc..39db573 100644 --- a/src/singleton/buffer.rs +++ b/src/singleton/buffer.rs @@ -13,45 +13,52 @@ use { } }; -pub struct SingletonBuffer -where T: Clone + Eq + Send + Sync + 'static { - value: T, - cast: Arc>>> -} +pub struct SingletonBufferView(Arc>); -impl View for SingletonBuffer +impl View for SingletonBufferView where T: Clone + Eq + Send + Sync + 'static { type Msg = (); } -impl SingletonView for SingletonBuffer +impl SingletonView for SingletonBufferView where T: Clone + Eq + Send + Sync + 'static { type Item = T; fn get(&self) -> Self::Item { - self.value.clone() + self.0.read().unwrap().clone() } } +pub struct SingletonBuffer +where T: Clone + Eq + Send + Sync + 'static { + value: Arc>, + cast: Arc>>> +} + impl SingletonBuffer where T: Clone + Eq + Send + Sync + 'static { pub fn new( value: T, port: InnerViewPort> - ) -> Arc> { - let buf = Arc::new(RwLock::new( - SingletonBuffer { - value, - cast: port.get_broadcast() - } - )); - port.set_view(Some(buf.clone())); - buf + ) -> Self { + let value = Arc::new(RwLock::new(value)); + port.set_view(Some(Arc::new(SingletonBufferView(value.clone())))); + + SingletonBuffer { + value, + cast: port.get_broadcast() + } + } + + pub fn get(&self) -> T { + self.value.read().unwrap().clone() } pub fn set(&mut self, new_value: T) { - if self.value != new_value { - self.value = new_value; + let mut v = self.value.write().unwrap(); + if *v != new_value { + *v = new_value; + drop(v); self.cast.notify(&()); } } diff --git a/src/string_editor.rs b/src/string_editor.rs index 7b20a89..07d62e0 100644 --- a/src/string_editor.rs +++ b/src/string_editor.rs @@ -1,145 +1,276 @@ - use { - std::{ - sync::{Arc, RwLock}, - }, - cgmath::Point2, + std::sync::{Arc, RwLock}, crate::{ - core::{ - ObserverExt, - ObserverBroadcast, - InnerViewPort - }, - index::{ImplIndexView}, - grid::{GridWindowIterator}, - terminal::{TerminalAtom, TerminalStyle, TerminalView}, - //vec_buffer::VecBuffer + core::{ViewPort, OuterViewPort, InnerViewPort}, + singleton::{SingletonView, SingletonBuffer}, + sequence::VecBuffer } }; //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> -pub struct StringEditorState { - cursor: usize, - data: Arc>> -} - -impl ImplIndexView for StringEditorState { - type Key = Point2; - type Value = TerminalAtom; - - fn get(&self, pos: &Point2) -> Option { - let data = self.data.read().unwrap(); - - if pos.y == 0 { - let i = pos.x as usize; - if i < data.len() + 3 { - return Some( - if i == 0 { - TerminalAtom::new('"', TerminalStyle::fg_color((180,200,130))) - } else if i-1 == self.cursor { - TerminalAtom::new('|', TerminalStyle::fg_color((180,200,130)).add(TerminalStyle::bold(false))) - } else if i-1 == data.len()+1 { - TerminalAtom::new('"', TerminalStyle::fg_color((180,200,130))) - } else { - TerminalAtom::new( - data.get(i as usize - if i <= self.cursor { 1 } else { 2 }).unwrap().clone(), - TerminalStyle::fg_color((80,150,80)).add(TerminalStyle::bold(true)) - ) - } - ) - } - } - - None - } - - fn area(&self) -> Option>> { - Some(GridWindowIterator::from( - Point2::new(0, 0) - .. Point2::new(self.data.read().unwrap().len() as i16 + 3, 1)).collect()) - } -} - pub struct StringEditor { - state: Arc>, - cast: Arc>> + data: VecBuffer, + cursor: SingletonBuffer, + + data_port: ViewPort>>, + cursor_port: ViewPort> } impl StringEditor { - pub fn new( - port: InnerViewPort - ) -> Self { - let state = Arc::new(RwLock::new(StringEditorState{ - cursor: 7, - data: Arc::new(RwLock::new("edit me".chars().collect())) - })); - - let cast = port.set_view(Some(state.clone())); + pub fn new() -> Self { + let data_port = ViewPort::new(); + let cursor_port = ViewPort::new(); StringEditor { - state, - cast + data: VecBuffer::new(data_port.inner()), + cursor: SingletonBuffer::new(0, cursor_port.inner()), + + data_port, + cursor_port } } - pub fn next(&mut self) { - let cur = self.state.read().unwrap().cursor; - self.goto(cur + 1); + pub fn get_data_port(&self) -> OuterViewPort>> { + self.data_port.outer() } - - pub fn prev(&mut self) { - let cur = self.state.read().unwrap().cursor; - if cur > 0 { - self.goto(cur - 1); + + pub fn get_cursor_port(&self) -> OuterViewPort> { + self.cursor_port.outer() + } + + pub fn goto(&mut self, new_pos: usize) { + if new_pos <= self.data.len() { + self.cursor.set(new_pos); } } pub fn goto_end(&mut self) { - let l = self.state.read().unwrap().data.read().unwrap().len(); - self.goto(l); + self.cursor.set(self.data.len()); } - pub fn goto(&mut self, mut new_idx: usize) { - let old_idx = { - let mut state = self.state.write().unwrap(); - let old_idx = state.cursor.clone(); - let len = state.data.read().unwrap().len(); - new_idx = std::cmp::min(new_idx, len); - state.cursor = new_idx; - old_idx - }; + pub fn prev(&mut self) { + let cur = self.cursor.get(); + if cur > 0 { + self.cursor.set(cur - 1); + } + } - self.cast.notify_each( - (std::cmp::min(old_idx, new_idx) ..= std::cmp::max(old_idx, new_idx)) - .map(|idx| Point2::new(1+idx as i16, 0)) - ); + pub fn next(&mut self) { + self.goto(self.cursor.get() + 1); } pub fn insert(&mut self, c: char) { - self.cast.notify_each({ - let state = self.state.write().unwrap(); - let mut data = state.data.write().unwrap(); - - data.insert(state.cursor, c); - - state.cursor .. data.len()+2 - }.map(|idx| Point2::new(1+idx as i16, 0))); - + self.data.insert(self.cursor.get(), c); self.next(); } pub fn delete(&mut self) { - self.cast.notify_each({ - let state = self.state.write().unwrap(); - let mut data = state.data.write().unwrap(); - - if state.cursor < data.len() { - data.remove(state.cursor); - } - - state.cursor .. data.len()+3 - }.map(|idx| Point2::new(1+idx as i16, 0))); + let cur = self.cursor.get(); + if cur < self.data.len() { + self.data.remove(cur); + } } } +//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>> + +pub mod insert_view { + use { + std::sync::{Arc, RwLock, Weak}, + cgmath::Point2, + crate::{ + core::{Observer, ObserverExt, ObserverBroadcast, OuterViewPort, InnerViewPort}, + singleton::SingletonView, + sequence::SequenceView, + index::ImplIndexView, + grid::GridWindowIterator, + terminal::{TerminalAtom, TerminalStyle, TerminalView} + } + }; + + struct CursorObserver { + cursor: Option>>, + edit: Weak> + } + + impl Observer> for CursorObserver { + fn reset(&mut self, new_cursor: Option>>) { + self.cursor = new_cursor; + + if let Some(cursor) = self.cursor.as_ref() { + self.edit + .upgrade().unwrap() + .write().unwrap() + .update_cursor( cursor.get() ); + } + } + + fn notify(&self, _msg: &()) { + if let Some(cursor) = self.cursor.as_ref() { + self.edit + .upgrade().unwrap() + .write().unwrap() + .update_cursor( cursor.get() ); + } + } + } + + struct DataObserver { + data: Option>>, + edit: Weak> + } + + impl Observer> for DataObserver { + fn reset(&mut self, new_data: Option>>) { + let old_len = + if let Some(data) = self.data.as_ref() { + data.len().unwrap_or(0) + } else { + 0 + }; + + self.data = new_data; + + let new_len = + if let Some(data) = self.data.as_ref() { + data.len().unwrap_or(0) + } else { + 0 + }; + + self.edit + .upgrade().unwrap() + .write().unwrap() + .reset_data( std::cmp::max(old_len, new_len) ); + } + + fn notify(&self, pos: &usize) { + self.edit + .upgrade().unwrap() + .write().unwrap() + .update_data( *pos ); + } + } + + pub struct StringEditView { + data_obs: Option>>, + cursor_obs: Option>>, + cur_pos: usize, + cast: Arc>> + } + + impl StringEditView { + pub fn new( + cursor_port: OuterViewPort>, + data_port: OuterViewPort>, + out_port: InnerViewPort + ) -> Arc> { + let edit_view = Arc::new(RwLock::new( + StringEditView { + data_obs: None, + cursor_obs: None, + cur_pos: 0, + cast: out_port.get_broadcast() + } + )); + + let data_obs = Arc::new(RwLock::new( + DataObserver { + data: None, + edit: Arc::downgrade(&edit_view) + } + )); + edit_view.write().unwrap().data_obs = Some(data_obs.clone()); + data_port.add_observer(data_obs); + + let cursor_obs = Arc::new(RwLock::new( + CursorObserver { + cursor: None, + edit: Arc::downgrade(&edit_view) + } + )); + edit_view.write().unwrap().cursor_obs = Some(cursor_obs.clone()); + cursor_port.add_observer(cursor_obs); + + out_port.set_view(Some(edit_view.clone())); + edit_view + } + + fn reset_data(&mut self, max_len: usize) { + self.cast.notify_each(GridWindowIterator::from( + Point2::new(0, 0) .. Point2::new(max_len as i16 + 1, 1) + )); + } + + fn update_data(&mut self, pos: usize) { + self.cast.notify( + &Point2::new( + if pos < self.cur_pos { + pos + } else { + pos + 1 + } as i16, + 0 + ) + ); + } + + fn update_cursor(&mut self, new_pos: usize) { + let old_pos = self.cur_pos; + self.cur_pos = new_pos; + + self.cast.notify_each(GridWindowIterator::from( + Point2::new(std::cmp::min(old_pos,new_pos) as i16, 0) .. Point2::new(std::cmp::max(old_pos,new_pos) as i16 + 1, 1) + )); + } + } + + impl ImplIndexView for StringEditView { + type Key = Point2; + type Value = TerminalAtom; + + fn get(&self, pos: &Point2) -> Option { + if pos.y == 0 && pos.x >= 0 { + let i = pos.x as usize; + let data = + self.data_obs.as_ref().unwrap() + .read().unwrap() + .data.clone() + .unwrap(); + let len = data.len().unwrap(); + + if i < len+1 { + return Some( + if i < self.cur_pos && i < len { + TerminalAtom::from(data.get(&i).unwrap()) + } else if i == self.cur_pos { + TerminalAtom::new('|', TerminalStyle::fg_color((200, 0, 0))) + } else { + TerminalAtom::from(data.get(&(i-1)).unwrap()) + } + ); + } + } + + None + } + + fn area(&self) -> Option>> { + let data = + self.data_obs.as_ref().unwrap() + .read().unwrap() + .data.clone() + .unwrap(); + let len = data.len()?; + + Some( + GridWindowIterator::from( + Point2::new(0, 0) .. Point2::new(len as i16 + 1, 1) + ).collect() + ) + } + } +} + +