From 0962b96c3a425099266cebadbe916d844933def8 Mon Sep 17 00:00:00 2001
From: Michael Sippel <micha@fragmental.art>
Date: Sun, 6 Jun 2021 18:44:41 +0200
Subject: [PATCH] add first list decorator projection

---
 shell/src/list.rs | 167 ++++++++++++++++++++++++++++++++++++++++++++++
 shell/src/main.rs |  66 ++++++++++++++----
 2 files changed, 220 insertions(+), 13 deletions(-)
 create mode 100644 shell/src/list.rs

diff --git a/shell/src/list.rs b/shell/src/list.rs
new file mode 100644
index 0000000..da29c73
--- /dev/null
+++ b/shell/src/list.rs
@@ -0,0 +1,167 @@
+
+use {
+    std::sync::Arc,
+    std::sync::RwLock,
+    nested::{
+        core::{
+            View,
+            ViewPort,
+            OuterViewPort,
+            InnerViewPort,
+            Observer,
+            ObserverBroadcast
+        },
+        sequence::{SequenceView, VecBuffer},
+        terminal::{Terminal, TerminalAtom, TerminalStyle},
+        projection::ProjectionHelper
+    }
+};
+
+pub struct ListDecorator {
+    opening_port: OuterViewPort<dyn SequenceView<Item = char>>,
+    closing_port: OuterViewPort<dyn SequenceView<Item = char>>,
+    delim_port: OuterViewPort<dyn SequenceView<Item = char>>,
+    items: Arc<dyn SequenceView<Item = OuterViewPort<dyn SequenceView<Item = TerminalAtom>>>>,
+    list_style: TerminalStyle,
+    item_style: TerminalStyle,
+
+    cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = OuterViewPort<dyn SequenceView<Item = TerminalAtom>>>>>>,
+    proj_helper: ProjectionHelper<Self>
+}
+
+impl View for ListDecorator {
+    type Msg = usize;
+}
+
+impl SequenceView for ListDecorator {
+    type Item = OuterViewPort<dyn SequenceView<Item = TerminalAtom>>;
+
+    fn len(&self) -> Option<usize> {
+        Some(self.items.len()? * 2 + 1)
+    }
+
+    fn get(&self, idx: &usize) -> Option<Self::Item> {
+        let item_idx = idx / 2;
+        let list_style = self.list_style.clone();
+        let item_style = self.item_style.clone();        
+        Some(
+            if idx % 2 == 0 {
+                if item_idx == 0 {
+                    self.opening_port.clone()
+                } else if item_idx == self.items.len().unwrap_or(0) {
+                    self.closing_port.clone()
+                } else {
+                    self.delim_port.clone()
+                }
+                .map(move |c| TerminalAtom::new(*c, list_style))
+            } else {
+                self.items
+                    .get(&item_idx)?
+                    .map(move |atom| atom.add_style_back(item_style))
+            }
+        )
+    }
+}
+
+impl ListDecorator {
+    pub fn new(
+        opening_port: OuterViewPort<dyn SequenceView<Item = char>>,
+        closing_port: OuterViewPort<dyn SequenceView<Item = char>>,
+        delim_port: OuterViewPort<dyn SequenceView<Item = char>>,
+        items_port: OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn SequenceView<Item = TerminalAtom>>>>,
+        list_style: TerminalStyle,
+        item_style: TerminalStyle,
+        out_port: InnerViewPort<dyn SequenceView<Item = OuterViewPort<dyn SequenceView<Item = TerminalAtom>>>>
+    ) -> Arc<RwLock<Self>> {
+        let mut proj_helper = ProjectionHelper::new(out_port.0.update_hooks.clone());
+        
+        let li = Arc::new(RwLock::new(ListDecorator {
+            opening_port,
+            closing_port,
+            delim_port,
+            items: proj_helper.new_sequence_arg(
+                items_port,
+                |s: &mut Self, item_idx| {
+                    s.cast.notify(&(item_idx * 2));
+                    s.cast.notify(&(item_idx * 2 + 1));
+                }
+            ),
+            list_style,
+            item_style,
+            cast: out_port.get_broadcast(),
+            proj_helper
+        }));
+
+        out_port.set_view(Some(li.clone()));
+        li
+    }
+
+    pub fn lisp_style(
+        level: usize,
+        items_port: OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn SequenceView<Item = TerminalAtom>>>>,
+        out_port: InnerViewPort<dyn SequenceView<Item = OuterViewPort<dyn SequenceView<Item = TerminalAtom>>>>
+    ) -> Arc<RwLock<Self>> {
+        let opening_port = ViewPort::new();
+        let opening = VecBuffer::<char>::with_data("(".chars().collect(), opening_port.inner());
+
+        let closing_port = ViewPort::new();
+        let closing = VecBuffer::<char>::with_data(")".chars().collect(), closing_port.inner());
+
+        let delim_port = ViewPort::new();
+        let delim = VecBuffer::<char>::with_data(" ".chars().collect(), delim_port.inner());
+
+        Self::new(
+            opening_port.outer().to_sequence(),
+            closing_port.outer().to_sequence(),
+            delim_port.outer().to_sequence(),
+            items_port,
+            TerminalStyle::fg_color(
+                match level {
+                    0 => (200, 120, 10),
+                    1 => (120, 200, 10),
+                    _ => (255, 255, 255)
+                }
+            ),
+            TerminalStyle::fg_color(
+                match level {
+                    _ => (255, 255, 255)
+                }
+            ),
+            out_port
+        )
+    }
+
+    pub fn c_style(
+        level: usize,
+        items_port: OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn SequenceView<Item = TerminalAtom>>>>,
+        out_port: InnerViewPort<dyn SequenceView<Item = OuterViewPort<dyn SequenceView<Item = TerminalAtom>>>>
+    ) -> Arc<RwLock<Self>> {
+        let opening_port = ViewPort::new();
+        let opening = VecBuffer::<char>::with_data("{".chars().collect(), opening_port.inner());
+
+        let closing_port = ViewPort::new();
+        let closing = VecBuffer::<char>::with_data("}".chars().collect(), closing_port.inner());
+
+        let delim_port = ViewPort::new();
+        let delim = VecBuffer::<char>::with_data(", ".chars().collect(), delim_port.inner());
+
+        Self::new(
+            opening_port.outer().to_sequence(),
+            closing_port.outer().to_sequence(),
+            delim_port.outer().to_sequence(),
+            items_port,
+            TerminalStyle::fg_color(
+                match level {
+                    _ => (255, 255, 255)
+                }
+            ),
+            TerminalStyle::fg_color(
+                match level {
+                    _ => (255, 255, 255)
+                }
+            ),
+            out_port
+        )
+    }
+}
+
diff --git a/shell/src/main.rs b/shell/src/main.rs
index 96f8f50..a324156 100644
--- a/shell/src/main.rs
+++ b/shell/src/main.rs
@@ -24,6 +24,8 @@ use {
     }
 };
 
+pub mod list;
+
 #[async_std::main]
 async fn main() {
     /* todo:
@@ -282,7 +284,7 @@ write::
             let hex_label = VecBuffer::<char>::with_data("x0".chars().collect(), hex_label_port.inner());
             
             let delim_port = ViewPort::new();
-            let delim = VecBuffer::<char>::with_data(" ,".chars().collect(), delim_port.inner());
+            let delim = VecBuffer::<char>::with_data(",".chars().collect(), delim_port.inner());
 
             let closing_port = ViewPort::new();
             let closing = VecBuffer::<char>::with_data("[".chars().collect(), closing_port.inner());
@@ -337,17 +339,55 @@ write::
                 );
             }
 
-            compositor.write().unwrap().push(
-                arg1_hex_unic_port
-                    .to_index()
-                    .map_key(
-                        |idx| Point2::new(40 - *idx as i16, 3 as i16),
-                        |pt| if pt.y == 3 { Some(40 - pt.x as usize) } else { None }
-                    )
-                    .map_item(
-                        |_idx, digit| TerminalAtom::from(digit)
-                    )
-            );
+            {
+                let items_port = ViewPort::new();
+                let items = VecBuffer::with_data(
+                    vec![
+                        arg1_dec_mint_port
+                            .map(|val| char::from_digit(*val as u32, 16).unwrap())
+                            .map(|c| TerminalAtom::from(c)),
+                        arg1_hex_unic_port.clone()
+                            .map(|c| TerminalAtom::from(c)),
+                        arg1_hex_unic_port.clone()
+                            .map(|c| TerminalAtom::from(c)),
+                    ],
+                    items_port.inner()
+                );
+
+                let liport = ViewPort::new();
+                let list_decorator = list::ListDecorator::lisp_style(
+                    1,
+                    items_port.outer().to_sequence(),
+                    liport.inner()
+                );
+
+                let par_items_port = ViewPort::new();
+                let par_items = VecBuffer::with_data(
+                    vec![
+                        liport.outer().flatten(),
+                        arg1_hex_unic_port.clone()
+                            .map(|c| TerminalAtom::from(c)),
+                    ],
+                    par_items_port.inner()
+                );
+
+                let par_liport = ViewPort::new();
+                let par_list_decorator = list::ListDecorator::lisp_style(
+                    0,
+                    par_items_port.outer().to_sequence(),
+                    par_liport.inner()
+                );
+
+                compositor.write().unwrap().push(
+                    par_liport.outer()
+                        .flatten()
+                        .to_index()
+                        .map_key(
+                            |idx| Point2::new(*idx as i16, 3),
+                            |pt| if pt.y == 3 { Some(pt.x as usize) } else { None }
+                        )
+                );
+            }
 
             let magic_vec_port = ViewPort::new();
             let _magic_vec = VecBuffer::with_data("<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>".chars().collect::<Vec<char>>(), magic_vec_port.inner());
@@ -393,7 +433,7 @@ write::
                     TerminalEvent::Input(Event::Key(Key::Home)) => ed.goto_end(),
                     TerminalEvent::Input(Event::Key(Key::End)) => ed.goto(0),
                     TerminalEvent::Input(Event::Key(Key::Char('\n'))) => {},
-                    TerminalEvent::Input(Event::Key(Key::Char(c))) => { ed.insert(c); ed.prev() },
+                    TerminalEvent::Input(Event::Key(Key::Char(c))) => { ed.insert(c); ed.prev(); },
                     TerminalEvent::Input(Event::Key(Key::Delete)) => ed.delete_prev(),
                     TerminalEvent::Input(Event::Key(Key::Backspace)) => ed.delete(),
                     TerminalEvent::Input(Event::Key(Key::Ctrl('c'))) => break,