From f118eada3464a22059cccc9f2c44cffb2b02e475 Mon Sep 17 00:00:00 2001
From: Michael Sippel <micha@fragmental.art>
Date: Sun, 8 May 2022 23:30:49 +0200
Subject: [PATCH] product editor

---
 nested/src/core/context.rs       |  44 ++-
 nested/src/core/mod.rs           |   2 +-
 nested/src/core/type_term.rs     |  11 +-
 nested/src/core/view.rs          |   2 +-
 nested/src/grid/flatten.rs       |   4 +-
 nested/src/integer/editor.rs     |   5 +
 nested/src/lib.rs                |   2 +
 nested/src/list/editor.rs        | 125 +++++----
 nested/src/list/sexpr.rs         |   1 +
 nested/src/make_editor.rs        | 113 ++++++++
 nested/src/product/editor.rs     | 456 +++++++++++++++++++++++++++++++
 nested/src/product/element.rs    |  74 +++++
 nested/src/product/mod.rs        |   4 +
 nested/src/singleton/to_index.rs |   4 +-
 nested/src/string_editor.rs      |  24 +-
 nested/src/tree_nav.rs           |  16 +-
 nested/src/vec/mod.rs            |   2 +-
 17 files changed, 821 insertions(+), 68 deletions(-)
 create mode 100644 nested/src/make_editor.rs
 create mode 100644 nested/src/product/editor.rs
 create mode 100644 nested/src/product/element.rs
 create mode 100644 nested/src/product/mod.rs

diff --git a/nested/src/core/context.rs b/nested/src/core/context.rs
index 5519265..a783512 100644
--- a/nested/src/core/context.rs
+++ b/nested/src/core/context.rs
@@ -1,7 +1,12 @@
 use {
-    crate::core::{
-        type_term::{TypeDict, TypeTerm},
-        AnyOuterViewPort, OuterViewPort, View,
+    crate::{
+        core::{
+            type_term::{TypeDict, TypeTerm, TypeID},
+            AnyOuterViewPort, OuterViewPort, View,
+        },
+        tree_nav::{
+            TerminalTreeEditor
+        }
     },
     std::{
         collections::HashMap,
@@ -232,10 +237,20 @@ pub struct MorphismType {
 //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
 
 pub struct Context {
+    /// assigns a name to every type
     type_dict: TypeDict,
+    
+    /// objects
+    objects: HashMap<String, Object>,
+
+    /// editors
+    editor_ctors: HashMap<TypeID, Box<dyn Fn(&Self, TypeTerm) -> Option<Arc<RwLock<dyn TerminalTreeEditor>>> + Send + Sync>>,
+
+    /// morphisms
     default_constructors: HashMap<TypeTerm, Box<dyn Fn() -> Object + Send + Sync>>,
     morphism_constructors: HashMap<MorphismType, Box<dyn Fn(Object) -> Object + Send + Sync>>,
-    objects: HashMap<String, Object>,
+
+    /// recursion
     parent: Option<Arc<RwLock<Context>>>,
 }
 
@@ -243,6 +258,7 @@ impl Context {
     pub fn with_parent(parent: Option<Arc<RwLock<Context>>>) -> Self {
         Context {
             type_dict: TypeDict::new(),
+            editor_ctors: HashMap::new(),
             default_constructors: HashMap::new(),
             morphism_constructors: HashMap::new(),
             objects: HashMap::new(),
@@ -261,7 +277,27 @@ impl Context {
     pub fn type_term_from_str(&self, tn: &str) -> Option<TypeTerm> {
         self.type_dict.type_term_from_str(&tn)
     }
+    pub fn type_term_to_str(&self, t: &TypeTerm) -> String {
+        self.type_dict.type_term_to_str(&t)
+    }
 
+    pub fn add_editor_ctor(&mut self, tn: &str, mk_editor: Box<dyn Fn(&Self, TypeTerm) -> Option<Arc<RwLock<dyn TerminalTreeEditor>>> + Send + Sync>) {
+        if let Some(tid) = self.type_dict.get_typeid(&tn.into()) {
+            self.editor_ctors.insert(tid, mk_editor);
+        } else {
+            println!("invalid type name");
+        }
+    }
+
+    pub fn make_editor(&self, type_term: TypeTerm) -> Option<Arc<RwLock<dyn TerminalTreeEditor>>> {
+        if let TypeTerm::Type{ id, args } = type_term.clone() {
+            let mk_editor = self.editor_ctors.get(&id)?;
+            mk_editor(self, type_term)
+        } else {
+            None
+        }
+    }
+    
     pub fn add_morphism(
         &mut self,
         morph_type: MorphismType,
diff --git a/nested/src/core/mod.rs b/nested/src/core/mod.rs
index 5197fed..4c0ba30 100644
--- a/nested/src/core/mod.rs
+++ b/nested/src/core/mod.rs
@@ -12,6 +12,6 @@ pub use {
     port::{
         AnyInnerViewPort, AnyOuterViewPort, AnyViewPort, InnerViewPort, OuterViewPort, ViewPort,
     },
-    type_term::{TypeDict, TypeID, TypeTerm},
+    type_term::{TypeDict, TypeID, TypeTerm, TypeLadder},
     view::View,
 };
diff --git a/nested/src/core/type_term.rs b/nested/src/core/type_term.rs
index c691843..da064a8 100644
--- a/nested/src/core/type_term.rs
+++ b/nested/src/core/type_term.rs
@@ -3,6 +3,7 @@ use {crate::bimap::Bimap, std::collections::HashMap};
 //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
 
 pub type TypeID = u64;
+pub type TypeLadder = Vec<TypeTerm>;
 
 //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
 
@@ -105,7 +106,7 @@ impl TypeTerm {
     pub fn to_str(&self, names: &HashMap<u64, String>) -> String {
         match self {
             TypeTerm::Type { id, args } => format!(
-                "( {} {})",
+                "« {} {}»",
                 names[id],
                 if args.len() > 0 {
                     args.iter().fold(String::new(), |str, term| {
@@ -141,6 +142,14 @@ impl TypeDict {
         self.type_id_counter += 1;
     }
 
+    pub fn get_typeid(&self, tn: &String) -> Option<TypeID> {
+        if let Some(id) = self.typenames.mλ.get(tn) {
+            Some(*id)
+        } else {
+            None
+        }
+    }
+
     pub fn type_term_from_str(&self, typename: &str) -> Option<TypeTerm> {
         TypeTerm::from_str(typename, &self.typenames.mλ)
     }
diff --git a/nested/src/core/view.rs b/nested/src/core/view.rs
index 204c4b8..109999d 100644
--- a/nested/src/core/view.rs
+++ b/nested/src/core/view.rs
@@ -1,4 +1,4 @@
-/*\
+                    /*\
 <<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
                    View
 <<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
diff --git a/nested/src/grid/flatten.rs b/nested/src/grid/flatten.rs
index 9f1d52d..3fffc18 100644
--- a/nested/src/grid/flatten.rs
+++ b/nested/src/grid/flatten.rs
@@ -198,11 +198,11 @@ where
         let old_limit = self.limit;
         self.limit = Point2::new(
             (0..=top_range.end().x as usize)
-                .map(|x| col_widths[x])
+                .map(|x| col_widths.get(x).unwrap_or(&0))
                 .sum::<i16>()
                 - 1,
             (0..=top_range.end().y as usize)
-                .map(|y| row_heights[y])
+                .map(|y| row_heights.get(y).unwrap_or(&0))
                 .sum::<i16>()
                 - 1,
         );
diff --git a/nested/src/integer/editor.rs b/nested/src/integer/editor.rs
index dd311c3..4f65b84 100644
--- a/nested/src/integer/editor.rs
+++ b/nested/src/integer/editor.rs
@@ -77,6 +77,8 @@ impl TerminalEditor for DigitEditor {
     }
 }
 
+impl TerminalTreeEditor for DigitEditor {}
+
 pub struct PosIntEditor {
     radix: u32,
     digits_editor:
@@ -182,3 +184,6 @@ impl TerminalEditor for PosIntEditor {
         }
     }
 }
+
+impl TerminalTreeEditor for PosIntEditor {}
+
diff --git a/nested/src/lib.rs b/nested/src/lib.rs
index 06a7f80..bda3e80 100644
--- a/nested/src/lib.rs
+++ b/nested/src/lib.rs
@@ -6,11 +6,13 @@ pub mod projection;
 pub mod grid;
 pub mod index;
 pub mod integer;
+pub mod product;
 pub mod list;
 pub mod sequence;
 pub mod singleton;
 pub mod terminal;
 pub mod vec;
+pub mod make_editor;
 
 pub mod tree_nav;
 
diff --git a/nested/src/list/editor.rs b/nested/src/list/editor.rs
index 40f551c..849f1a1 100644
--- a/nested/src/list/editor.rs
+++ b/nested/src/list/editor.rs
@@ -22,6 +22,7 @@ use {
 pub enum ListEditorStyle {
     HorizontalSexpr,
     VerticalSexpr,
+    Tuple(usize),
     Path,
     String,
     Clist,
@@ -189,6 +190,7 @@ where
                                 return TreeNavResult::Continue;
                             }
 
+                            self.cursor.set(ListCursor::default());
                             TreeNavResult::Exit
                         }
                     }
@@ -201,7 +203,7 @@ where
     }
 
     fn goto_home(&mut self) -> TreeNavResult {
-        let cur = self.cursor.get();
+        let mut cur = self.cursor.get();
         if self.data.len() == 0 && cur.idx.is_none() {
             self.cursor.set(ListCursor {
                 mode: ListCursorMode::Insert,
@@ -228,38 +230,38 @@ where
                 }
             }
             ListCursorMode::Modify => {
-                let ce = self.get_item().unwrap();
-                let mut cur_edit = ce.write().unwrap();
-                let cur_mode = cur_edit.get_cursor().leaf_mode;
-                let depth = cur_edit.get_cursor().tree_addr.len();
+                if let Some(ce) = self.get_item() {
+                    let mut cur_edit = ce.write().unwrap();
+                    let cur_mode = cur_edit.get_cursor().leaf_mode;
+                    let depth = cur_edit.get_cursor().tree_addr.len();
 
-                match cur_edit.goto_home() {
-                    TreeNavResult::Exit => {
-                        drop(cur_edit);
+                    match cur_edit.goto_home() {
+                        TreeNavResult::Exit => {
+                            drop(cur_edit);
 
-                        if let Some(i) = cur.idx {
-                            self.up();
+                            if let Some(i) = cur.idx {
+                                if i > 0 {
+                                    self.set_mode(ListCursorMode::Select);
+                                    self.pxev();
 
-                            if i > 0 {
-                                self.set_mode(ListCursorMode::Select);
-                                self.pxev();
+                                    for _x in 1..depth {
+                                        self.dn();
+                                        self.goto_end();
+                                    }
 
-                                for _x in 1..depth {
                                     self.dn();
-                                    self.goto_end();
+                                    self.set_leaf_mode(cur_mode);
+                                    //self.goto_home();
+                                    return TreeNavResult::Continue;
                                 }
-
-                                self.set_leaf_mode(cur_mode);
-                                self.dn();
-                                self.goto_home();
-                                return TreeNavResult::Continue;
                             }
                         }
-
-                        TreeNavResult::Exit
-                    }
-                    TreeNavResult::Continue => TreeNavResult::Continue,
+                        TreeNavResult::Continue => { return TreeNavResult::Continue; }
+                    };
                 }
+
+                self.cursor.set(ListCursor::default());
+                TreeNavResult::Exit
             }
         }
     }
@@ -279,34 +281,42 @@ where
                     TreeNavResult::Continue => {}
                 }
 
-                return TreeNavResult::Continue;
+                TreeNavResult::Continue
+            } else {
+                TreeNavResult::Exit
             }
+        } else {
+            self.cursor.set(ListCursor {
+                mode: cur.mode,
+                idx: None,
+            });
+            TreeNavResult::Exit
         }
-
-        self.cursor.set(ListCursor {
-            mode: cur.mode,
-            idx: None,
-        });
-        TreeNavResult::Exit
     }
 
     fn dn(&mut self) -> TreeNavResult {
-        let cur = self.cursor.get();
-        match cur.mode {
-            ListCursorMode::Insert | ListCursorMode::Select => {
-                if let Some(i) = cur.idx {
-                    if i < self.data.len() {
-                        self.set_mode(ListCursorMode::Modify);
-                        self.data.get_mut(i).write().unwrap().goto(TreeCursor {
-                            leaf_mode: cur.mode,
-                            tree_addr: vec![],
-                        });
-                        *self.cur_dist.write().unwrap() += 1;
+        let mut cur = self.cursor.get();
+
+        if cur.idx.is_none() {
+            self.goto_home()
+        } else {
+            match cur.mode {
+                ListCursorMode::Insert | ListCursorMode::Select => {
+                    if let Some(i) = cur.idx {
+                        if i < self.data.len() {
+                            self.set_mode(ListCursorMode::Modify);
+                            self.data.get_mut(i).write().unwrap().goto(TreeCursor {
+                                leaf_mode: cur.mode,
+                                tree_addr: vec![],
+                            });
+                            self.data.get_mut(i).write().unwrap().dn();
+                            *self.cur_dist.write().unwrap() += 1;
+                        }
                     }
+                    TreeNavResult::Continue
                 }
-                TreeNavResult::Continue
+                ListCursorMode::Modify => self.get_item().unwrap().write().unwrap().dn(),
             }
-            ListCursorMode::Modify => self.get_item().unwrap().write().unwrap().dn(),
         }
     }
 
@@ -397,7 +407,7 @@ where
                         TreeNavResult::Exit => {
                             drop(cur_edit);
                             drop(ce);
-                            self.up();
+                            //self.up();
 
                             if i + 1 < self.data.len() {
                                 self.set_mode(ListCursorMode::Select);
@@ -405,14 +415,14 @@ where
 
                                 for _x in 1..depth {
                                     self.dn();
-                                    self.goto_home();
                                 }
 
                                 self.set_leaf_mode(cur_mode);
                                 self.dn();
-                                self.goto_home();
+
                                 TreeNavResult::Continue
                             } else {
+                                self.cursor.set(ListCursor::default());
                                 TreeNavResult::Exit
                             }
                         }
@@ -435,6 +445,7 @@ where
         match self.style {
             ListEditorStyle::HorizontalSexpr => self.horizontal_sexpr_view(),
             ListEditorStyle::VerticalSexpr => self.vertical_sexpr_view(),
+            ListEditorStyle::Tuple(depth) => self.tuple_view(depth),
             ListEditorStyle::Path => self.path_view(),
             ListEditorStyle::String => self.string_view(),
             ListEditorStyle::Clist => self.clist_view(),
@@ -474,10 +485,10 @@ where
                     _ => {
                         let new_edit = (self.make_item_editor)();
                         self.data.insert(idx, new_edit.clone());
-                        self.dn();
-                        self.goto_home();
+                        self.set_mode(ListCursorMode::Modify);
+                        let mut ne = new_edit.write().unwrap();
 
-                        match new_edit.write().unwrap().handle_terminal_event(event) {
+                        match ne.handle_terminal_event(event) {
                             TerminalEditorResult::Exit => {
                                 self.cursor.set(ListCursor {
                                     mode: ListCursorMode::Insert,
@@ -552,6 +563,12 @@ where
     }
 }
 
+impl<ItemEditor, FnMakeItemEditor> TerminalTreeEditor for ListEditor<ItemEditor, FnMakeItemEditor>
+where
+    ItemEditor: TerminalTreeEditor + ?Sized + Send + Sync + 'static,
+    FnMakeItemEditor: Fn() -> Arc<RwLock<ItemEditor>> + Send + Sync,
+{}
+
 impl<ItemEditor, FnMakeItemEditor> ListEditor<ItemEditor, FnMakeItemEditor>
 where
     ItemEditor: TerminalTreeEditor + ?Sized + Send + Sync + 'static,
@@ -633,6 +650,13 @@ where
             .flatten()
     }
 
+    pub fn tuple_view(&self, depth: usize) -> OuterViewPort<dyn TerminalView> {
+        self.get_seg_seq_view()
+            .decorate("(", ")", ", ", depth)
+            .to_grid_horizontal()
+            .flatten()
+    }
+
     pub fn hex_view(&self) -> OuterViewPort<dyn TerminalView> {
         self.get_seg_seq_view()
             .decorate("0x", "", "", 0)
@@ -738,3 +762,4 @@ where
         self.goto(c);
     }
 }
+
diff --git a/nested/src/list/sexpr.rs b/nested/src/list/sexpr.rs
index 70ffc3d..53c374f 100644
--- a/nested/src/list/sexpr.rs
+++ b/nested/src/list/sexpr.rs
@@ -224,6 +224,7 @@ impl VerticalSexprDecorator {
             list_style: TerminalStyle::fg_color(match level {
                 0 => (200, 120, 10),
                 1 => (120, 200, 10),
+                2 => (200, 10, 120),
                 _ => (255, 255, 255),
             }),
             item_style: TerminalStyle::fg_color(match level {
diff --git a/nested/src/make_editor.rs b/nested/src/make_editor.rs
new file mode 100644
index 0000000..975f741
--- /dev/null
+++ b/nested/src/make_editor.rs
@@ -0,0 +1,113 @@
+
+use {
+    crate::{
+        core::{ViewPort, OuterViewPort, Observer, port::UpdateTask, TypeTerm, TypeLadder, Context},
+        terminal::{
+            Terminal, TerminalAtom, TerminalCompositor, TerminalEditor,
+            TerminalEditorResult, TerminalEvent, TerminalStyle, TerminalView,
+            make_label
+        },
+        sequence::{SequenceView},
+        tree_nav::{TreeNav, TerminalTreeEditor, TreeCursor, TreeNavResult},
+        vec::{VecBuffer, MutableVecAccess},
+        index::buffer::IndexBuffer,
+        integer::PosIntEditor,
+        string_editor::{StringEditor, CharEditor},
+        list::{ListEditor, ListCursorMode, ListEditorStyle},
+        product::editor::ProductEditor
+    },
+    cgmath::{Point2, Vector2},
+    std::{sync::{Arc, RwLock}, ops::{Deref, DerefMut}},
+    termion::event::{Event, Key},
+};
+
+pub fn make_editor(ctx: Arc<RwLock<Context>>, t: &TypeLadder, depth: usize) -> Arc<RwLock<dyn TerminalTreeEditor + Send + Sync>> {
+    let c = ctx.read().unwrap();
+    if t[0] == c.type_term_from_str("( PosInt 16 BigEndian )").unwrap() {
+        Arc::new(RwLock::new(PosIntEditor::new(16))) as Arc<RwLock<dyn TerminalTreeEditor + Send + Sync>>
+
+    } else if t[0] == c.type_term_from_str("( PosInt 10 BigEndian )").unwrap() {
+        Arc::new(RwLock::new(PosIntEditor::new(10))) as Arc<RwLock<dyn TerminalTreeEditor + Send + Sync>>
+
+    } else if t[0] == c.type_term_from_str("( String )").unwrap() {
+        Arc::new(RwLock::new(StringEditor::new())) as Arc<RwLock<dyn TerminalTreeEditor + Send + Sync>>
+
+    } else if t[0] == c.type_term_from_str("( List Char )").unwrap() {
+        Arc::new(RwLock::new(ListEditor::new(
+            || { Arc::new(RwLock::new(CharEditor::new())) },
+            ListEditorStyle::Plain
+        ))) as Arc<RwLock<dyn TerminalTreeEditor + Send + Sync>>
+
+    } else if t[0] == c.type_term_from_str("( List ℕ )").unwrap() {
+        Arc::new(RwLock::new(ListEditor::new(
+            || {
+                Arc::new(RwLock::new(PosIntEditor::new(16)))
+            },
+            ListEditorStyle::HorizontalSexpr
+        ))) as Arc<RwLock<dyn TerminalTreeEditor + Send + Sync>>
+
+    } else if t[0] == c.type_term_from_str("( Path )").unwrap() {
+        Arc::new(RwLock::new(ListEditor::new(
+            || {
+                Arc::new(RwLock::new(ListEditor::new(
+                    || {
+                        Arc::new(RwLock::new(CharEditor::new()))
+                    },
+                    ListEditorStyle::Plain
+                )))
+            },
+            ListEditorStyle::Path
+        ))) as Arc<RwLock<dyn TerminalTreeEditor + Send + Sync>>
+
+    } else if t[0] == c.type_term_from_str("( List RGB )").unwrap() {
+        Arc::new(RwLock::new(ListEditor::new({
+            let ctx = ctx.clone();
+            move || {
+                make_editor(ctx.clone(), &vec![ ctx.read().unwrap().type_term_from_str("( RGB )").unwrap() ], depth+1)
+            }
+        },
+            ListEditorStyle::VerticalSexpr
+        ))) as Arc<RwLock<dyn TerminalTreeEditor + Send + Sync>>
+
+    } else if t[0] == c.type_term_from_str("( RGB )").unwrap() {
+        Arc::new(RwLock::new(ProductEditor::new(depth, ctx.clone())
+                             .with_t("{ r: ")
+                             .with_n( vec![ ctx.read().unwrap().type_term_from_str("( PosInt 16 BigEndian )").unwrap() ] )
+                             .with_t(", g: ")
+                             .with_n( vec![ ctx.read().unwrap().type_term_from_str("( PosInt 16 BigEndian )").unwrap() ] )
+                             .with_t(", b: ")
+                             .with_n( vec![ ctx.read().unwrap().type_term_from_str("( PosInt 16 BigEndian )").unwrap() ] )
+                             .with_t(" }")
+        )) as Arc<RwLock<dyn TerminalTreeEditor + Send + Sync>>
+
+    } else if t[0] == c.type_term_from_str("( Vec3i )").unwrap() {
+        Arc::new(RwLock::new(ProductEditor::new(depth, ctx.clone())
+                             .with_t("{ x: ")
+                             .with_n( vec![ ctx.read().unwrap().type_term_from_str("( PosInt 10 BigEndian )").unwrap() ] )
+                             .with_t(", y: ")
+                             .with_n( vec![ ctx.read().unwrap().type_term_from_str("( PosInt 10 BigEndian )").unwrap() ] )
+                             .with_t(", z: ")
+                             .with_n( vec![ ctx.read().unwrap().type_term_from_str("( PosInt 10 BigEndian )").unwrap() ] )
+                             .with_t(" }")
+        )) as Arc<RwLock<dyn TerminalTreeEditor + Send + Sync>>
+
+    } else if t[0] == c.type_term_from_str("( List Term )").unwrap() {
+        Arc::new(RwLock::new(ListEditor::new({
+            let ctx = ctx.clone();
+            move || {
+                make_editor(ctx.clone(), &vec![ ctx.read().unwrap().type_term_from_str("( Term )").unwrap() ], depth+1)
+            }
+        },
+            ListEditorStyle::Tuple(depth)
+        ))) as Arc<RwLock<dyn TerminalTreeEditor + Send + Sync>>
+
+    } else { // else: term
+        Arc::new(RwLock::new(
+            ProductEditor::new(depth, ctx.clone())
+                .with_n( vec![ c.type_term_from_str("( List Char )").unwrap() ] )
+                .with_n( vec![ c.type_term_from_str("( List Term )").unwrap() ] )
+        )) as Arc<RwLock<dyn TerminalTreeEditor + Send + Sync>>
+    }
+}
+
+
diff --git a/nested/src/product/editor.rs b/nested/src/product/editor.rs
new file mode 100644
index 0000000..3a6a77e
--- /dev/null
+++ b/nested/src/product/editor.rs
@@ -0,0 +1,456 @@
+
+use {
+    crate::{
+        core::{ViewPort, OuterViewPort, Observer, port::UpdateTask, TypeTerm, TypeLadder, Context},
+        terminal::{
+            Terminal, TerminalAtom, TerminalCompositor, TerminalEditor,
+            TerminalEditorResult, TerminalEvent, TerminalStyle, TerminalView,
+            make_label
+        },
+        sequence::{SequenceView},
+        tree_nav::{TreeNav, TerminalTreeEditor, TreeCursor, TreeNavResult},
+        vec::{VecBuffer, MutableVecAccess},
+        index::buffer::IndexBuffer,
+        integer::PosIntEditor,
+        string_editor::{StringEditor, CharEditor},
+        list::{ListEditor, ListCursorMode, ListEditorStyle},
+        product::{element::ProductEditorElement},
+        make_editor::make_editor
+    },
+    cgmath::{Point2, Vector2},
+    std::{sync::{Arc, RwLock}, ops::{Deref, DerefMut}},
+    termion::event::{Event, Key},
+};
+
+pub struct ProductEditor {
+    elements: VecBuffer<ProductEditorElement>,
+    n_indices: Vec<usize>,
+    
+    el_port: OuterViewPort<dyn SequenceView<Item = ProductEditorElement>>,
+    el_view_port: OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>>,
+
+    ctx: Arc<RwLock<Context>>,
+    
+    cursor: Option<usize>,
+    depth: usize
+}
+
+impl ProductEditor {
+    pub fn new(depth: usize, ctx: Arc<RwLock<Context>>) -> Self {
+        let mut port = ViewPort::new();
+
+        let el_port = port.outer().to_sequence();
+        let el_view_port = el_port.map({
+            let ctx = ctx.clone();
+            move |e: &ProductEditorElement| { e.get_view(ctx.clone()) }
+        });
+
+        ProductEditor {
+            elements: VecBuffer::new(port.inner()),
+            el_port,
+            el_view_port,
+            n_indices: Vec::new(),
+
+            ctx,
+
+            cursor: None,
+            depth
+        }
+    }
+
+    pub fn with_t(mut self, t: &str) -> Self {
+        self.elements.push(ProductEditorElement::T(t.to_string()));
+        self
+    }
+
+    pub fn with_n(mut self, n: TypeLadder) -> Self {
+        let elem_idx = self.elements.len();
+        self.elements.push(ProductEditorElement::N{
+            t: n,
+            editor: None,
+            select: false
+        });
+        self.n_indices.push(elem_idx);
+        self
+    }
+
+    fn get_editor_element(&self, idx: usize) -> Option<ProductEditorElement> {
+        if let Some(i) = self.n_indices.get(idx) {
+            Some(self.elements.get(*i))
+        } else {
+            None
+        }
+    }
+
+    fn get_editor_element_mut(&mut self, idx: usize) -> Option<MutableVecAccess<ProductEditorElement>> {
+        if let Some(i) = self.n_indices.get(idx) {
+            Some(self.elements.get_mut(*i))
+        } else {
+            None
+        }
+    }
+
+    fn get_cur_element(&self) -> Option<ProductEditorElement> {
+        self.get_editor_element(self.cursor?)
+    }
+
+    fn get_cur_element_mut(&mut self) -> Option<MutableVecAccess<ProductEditorElement>> {
+        self.get_editor_element_mut(self.cursor?)
+    }
+
+    fn get_editor(&self, idx: usize) -> Option<Arc<RwLock<dyn TerminalTreeEditor + Send + Sync>>> {
+        if let Some(ProductEditorElement::N{ t: _, editor, select: _ }) = self.get_editor_element(idx) {
+            editor
+        } else {
+            None
+        }
+    }
+    
+    fn get_cur_editor(&self) -> Option<Arc<RwLock<dyn TerminalTreeEditor + Send + Sync>>> {
+        self.get_editor(self.cursor?)
+    }
+
+    fn set_leaf_mode(&mut self, mode: ListCursorMode) {
+        let mut c = self.get_cursor();
+        c.leaf_mode = mode;
+        self.goto(c);
+    }
+}
+
+impl TreeNav for ProductEditor {
+    fn get_cursor(&self) -> TreeCursor {
+        if let Some(i) = self.cursor {
+            if let Some(e) = self.get_editor(i) {
+                let mut c = e.read().unwrap().get_cursor();
+                if c.tree_addr.len() == 0 {
+                    c.leaf_mode = ListCursorMode::Select;
+                }
+                c.tree_addr.insert(0, i);                
+                c
+            } else {
+                TreeCursor {
+                    leaf_mode: ListCursorMode::Select,
+                    tree_addr: vec![ i ]
+                }
+            }
+        } else {
+            TreeCursor::default()
+        }
+    }
+
+    fn goto(&mut self, mut c: TreeCursor) -> TreeNavResult {
+        if let Some(mut element) = self.get_cur_element_mut() {
+            if let ProductEditorElement::N{ t, editor, select } = element.deref_mut() {
+                if let Some(e) = editor {
+                    e.write().unwrap().goto(TreeCursor::default());
+                }
+                *select = false;
+            }
+        }
+
+        if c.tree_addr.len() > 0 {
+            self.cursor = Some(c.tree_addr.remove(0));
+
+            if let Some(mut element) = self.get_cur_element_mut() {
+                if let ProductEditorElement::N{ t, editor, select } = element.deref_mut() {
+                    if let Some(e) = editor {
+                        e.write().unwrap().goto(c);
+                    }
+                    *select = true;
+                }
+            }
+
+            TreeNavResult::Continue
+        } else {
+            self.cursor = None;
+            TreeNavResult::Exit
+        }
+    }
+
+    fn goto_home(&mut self) -> TreeNavResult {
+        if let Some(c) = self.cursor {
+            if let Some(ProductEditorElement::N{ t, editor, select }) = self.get_cur_element_mut().as_deref_mut() {
+                if let Some(e) = editor {
+                    let mut ce = e.write().unwrap();
+
+                    let cur_mode = ce.get_cursor().leaf_mode;
+                    let depth = ce.get_cursor().tree_addr.len();
+
+                    if depth > 0 {
+                        return match ce.goto_home() {
+                            TreeNavResult::Exit => {
+                                drop(ce);
+                                *select = false;
+
+                                match self.pxev() {
+                                    TreeNavResult::Exit => TreeNavResult::Exit,
+                                    TreeNavResult::Continue => {
+                                        for _x in 1..depth {
+                                            self.dn();
+                                            self.goto_end();
+                                        }
+                                        self.dn();
+                                        self.set_leaf_mode(cur_mode);
+
+                                        TreeNavResult::Continue
+                                    }
+                                }
+                            },
+                            TreeNavResult::Continue => TreeNavResult::Continue
+                        };
+                    }
+                }
+
+                *select = false;
+                if c != 0 {
+                    self.cursor = Some(0);
+                    if let Some(ProductEditorElement::N{ t, editor, select }) = self.get_cur_element_mut().as_deref_mut() {
+                        *select = true;
+                    }
+                    return TreeNavResult::Continue;
+                }
+            }
+        }
+        self.cursor = None;
+        TreeNavResult::Exit
+    }
+
+    fn goto_end(&mut self) -> TreeNavResult {
+        if let Some(c) = self.cursor {
+            if let Some(ProductEditorElement::N{ t, editor, select }) = self.get_cur_element_mut().as_deref_mut() {
+                if let Some(e) = editor {
+                    let mut ce = e.write().unwrap();
+
+                    let cur_mode = ce.get_cursor().leaf_mode;
+                    let depth = ce.get_cursor().tree_addr.len();
+
+                    if depth > 0 {
+                        return match ce.goto_end() {
+                            TreeNavResult::Exit => {
+                                drop(ce);
+                                *select = false;
+
+                                match self.nexd() {
+                                    TreeNavResult::Exit => TreeNavResult::Exit,
+                                    TreeNavResult::Continue => {
+                                        for _x in 1..depth {
+                                            self.dn();
+                                        }
+
+                                        self.dn();
+                                        self.set_leaf_mode(cur_mode);
+                                        self.goto_end();
+
+                                        TreeNavResult::Continue
+                                    }
+                                }
+                            },
+                            TreeNavResult::Continue => TreeNavResult::Continue
+                        };
+                    }
+                }
+
+                *select = false;
+                if c < self.n_indices.len()-1 {
+                    self.cursor = Some(self.n_indices.len()-1);
+                    if let Some(ProductEditorElement::N{ t, editor, select }) = self.get_cur_element_mut().as_deref_mut() {
+                        *select = true;
+                    }
+                    return TreeNavResult::Continue;
+                }
+            }
+        }
+        self.cursor = None;
+        TreeNavResult::Exit
+    }
+
+    fn pxev(&mut self) -> TreeNavResult {
+        if let Some(c) = self.cursor {
+            if let Some(ProductEditorElement::N{ t, editor, select }) = self.get_editor_element_mut(c).as_deref_mut() {
+                if let Some(e) = editor {
+                    let mut ce = e.write().unwrap();
+
+                    let depth = ce.get_cursor().tree_addr.len();
+                    let cur_mode = ce.get_cursor().leaf_mode;
+
+                    if depth > 0 {
+                        return match ce.pxev() {
+                            TreeNavResult::Exit => {
+                                drop(ce);
+                                *select = false;
+
+                                if c > 0 {
+                                    self.cursor = Some(c-1);
+                                    if let Some(ProductEditorElement::N{ t, editor, select }) = self.get_cur_element_mut().as_deref_mut() {
+                                        *select = true;
+                                    }
+
+                                    for _x in 1..depth {
+                                        self.dn();
+                                        self.goto_end();
+                                    }
+
+                                    self.dn();
+                                    self.set_leaf_mode(cur_mode);
+                                    self.goto_end();
+
+                                    TreeNavResult::Continue                                
+                                } else {
+                                    TreeNavResult::Exit
+                                }
+                            }
+                            TreeNavResult::Continue => TreeNavResult::Continue
+                        };
+                    }
+                }
+
+                *select = false;
+                if c > 0 {
+                    self.cursor = Some(c-1);
+                    if let Some(ProductEditorElement::N{ t, editor, select }) = self.get_cur_element_mut().as_deref_mut() {
+                        *select = true;
+                    }
+                    return TreeNavResult::Continue;
+                }
+            }
+        }
+
+        self.cursor = None;
+        TreeNavResult::Exit
+    }
+
+    fn nexd(&mut self) -> TreeNavResult {
+        if let Some(c) = self.cursor.clone() {
+            if let Some(ProductEditorElement::N{ t, editor, select }) = self.get_editor_element_mut(c).as_deref_mut() {
+                if let Some(e) = editor {
+                    let mut ce = e.write().unwrap();
+
+                    let depth = ce.get_cursor().tree_addr.len();
+                    let cur_mode = ce.get_cursor().leaf_mode;
+
+                    if depth > 0 {
+                        return match ce.nexd() {
+                            TreeNavResult::Exit => {
+                                drop(ce);
+                                *select = false;
+
+                                if c+1 < self.n_indices.len() {
+                                    self.cursor = Some(c+1);
+                                    if let Some(ProductEditorElement::N{ t, editor, select }) = self.get_cur_element_mut().as_deref_mut() {
+                                        *select = true;
+                                    }
+
+                                    for _x in 1..depth {
+                                        self.dn();
+                                    }
+
+                                    self.dn();
+                                    self.set_leaf_mode(cur_mode);
+
+                                    TreeNavResult::Continue
+                                } else {
+                                    self.cursor = None;
+                                    TreeNavResult::Exit
+                                }
+                            }
+                            TreeNavResult::Continue => TreeNavResult::Continue
+                        };
+                    }
+                }
+
+                *select = false;
+                if c+1 < self.n_indices.len() {
+                    self.cursor = Some(c+1);
+                    if let Some(ProductEditorElement::N{ t, editor, select }) = self.get_cur_element_mut().as_deref_mut() {
+                        *select = true;
+                    }
+
+                    return TreeNavResult::Continue;
+                }
+            }
+        }
+
+        self.cursor = None;
+        TreeNavResult::Exit
+    }
+
+    fn up(&mut self) -> TreeNavResult {
+        if let Some(ProductEditorElement::N{ t, editor, select }) = self.get_cur_element_mut().as_deref_mut() {
+            if let Some(e) = editor {
+                let mut ce = e.write().unwrap();
+                if ce.get_cursor().tree_addr.len() > 0 {
+                    ce.up();
+                    return TreeNavResult::Continue;
+                }
+            }
+            *select = false;
+        }
+
+        self.cursor = None;
+        TreeNavResult::Exit
+    }
+
+    fn dn(&mut self) -> TreeNavResult {
+        if let Some(c) = self.cursor {
+            if let Some(ProductEditorElement::N{ t, editor, select }) = self.get_editor_element_mut(c).as_deref_mut() {
+                if let Some(e) = editor {
+                    e.write().unwrap().dn();
+                } else {
+                    let e = make_editor(self.ctx.clone(), t, self.depth+1);
+                    e.write().unwrap().goto_home();
+                    *editor = Some(e);
+                }
+            }
+        } else {
+            self.cursor = Some(0);
+            if let Some(ProductEditorElement::N{ t, editor, select }) = self.get_cur_element_mut().as_deref_mut() {
+                *select = true;
+            }
+        }
+
+        TreeNavResult::Continue
+    }
+}
+
+impl TerminalEditor for ProductEditor {
+    fn get_term_view(&self) -> OuterViewPort<dyn TerminalView> {
+        self.el_view_port.to_grid_horizontal().flatten()
+    }
+
+    fn handle_terminal_event(&mut self, event: &TerminalEvent) -> TerminalEditorResult {
+        if let Some(ProductEditorElement::N{ t, editor, select }) = self.get_cur_element_mut().as_deref_mut() {
+            *select = true;
+            if let Some(e) = editor.clone() {
+                match e.clone().write().unwrap().handle_terminal_event(event) {
+                    TerminalEditorResult::Exit =>
+                        match event {
+                            TerminalEvent::Input(Event::Key(Key::Backspace)) => {
+                                *editor = None;
+                                TerminalEditorResult::Continue
+                            }
+                            _ => {
+                                drop(e);
+                                match self.nexd() {
+                                    TreeNavResult::Continue => TerminalEditorResult::Continue,
+                                    TreeNavResult::Exit => TerminalEditorResult::Exit
+                                }
+                            }
+                        },
+                    TerminalEditorResult::Continue =>
+                    TerminalEditorResult::Continue
+                }
+            } else {
+                let e = make_editor(self.ctx.clone(), t, self.depth+1);
+                *editor = Some(e.clone());
+                e.write().unwrap().dn();
+                let x = e.write().unwrap().handle_terminal_event(event);
+                x
+            }
+        } else {
+            TerminalEditorResult::Exit
+        }
+    }
+}
+
+impl TerminalTreeEditor for ProductEditor {}
+
diff --git a/nested/src/product/element.rs b/nested/src/product/element.rs
new file mode 100644
index 0000000..3387f86
--- /dev/null
+++ b/nested/src/product/element.rs
@@ -0,0 +1,74 @@
+use {
+    crate::{
+        core::{ViewPort, OuterViewPort, Observer, port::UpdateTask, TypeTerm, TypeLadder, Context},
+        terminal::{
+            Terminal, TerminalAtom, TerminalCompositor, TerminalEditor,
+            TerminalEditorResult, TerminalEvent, TerminalStyle, TerminalView,
+            make_label
+        },
+        sequence::{SequenceView},
+        tree_nav::{TreeNav, TerminalTreeEditor, TreeCursor, TreeNavResult},
+        vec::{VecBuffer, MutableVecAccess},
+        index::buffer::IndexBuffer,
+        integer::PosIntEditor,
+        string_editor::{StringEditor, CharEditor},
+        list::{ListEditor, ListCursorMode, ListEditorStyle}
+    },
+    cgmath::{Point2, Vector2},
+    std::{sync::{Arc, RwLock}, ops::{Deref, DerefMut}},
+    termion::event::{Event, Key},
+};
+
+
+#[derive(Clone)]
+pub enum ProductEditorElement {
+    T( String ),
+    N {
+        t: TypeLadder,
+        editor: Option<Arc<RwLock<dyn TerminalTreeEditor + Send + Sync>>>,
+        select: bool
+    }
+}
+
+impl ProductEditorElement {
+    pub fn get_view(&self, ctx: Arc<RwLock<Context>>) -> OuterViewPort<dyn TerminalView> {
+        match self {
+            ProductEditorElement::T(t) =>
+                make_label(t.as_str())
+                .map_item(
+                    |i, x|
+                    x.add_style_back(TerminalStyle::fg_color((0,120,200)))
+                ),
+
+            ProductEditorElement::N {t: _, editor: Some(e), select} =>
+                e.read().unwrap()
+                .get_term_view()
+                .map_item({ let select = *select;
+                            move |i, x| x
+                            .add_style_back(TerminalStyle::fg_color((250,210,0)))
+                            .add_style_back(
+                                if select {
+                                    TerminalStyle::bg_color((40,40,40))
+                                } else {
+                                    TerminalStyle::default()
+                                }
+                            )
+                }),
+
+            ProductEditorElement::N{t, editor: None, select} =>
+                make_label(&ctx.read().unwrap().type_term_to_str(&t[0]))
+                .map_item({ let select = *select;
+                            move |i, x| x
+                            .add_style_back(TerminalStyle::fg_color((130,90,40)))
+                            .add_style_back(
+                                if select {
+                                    TerminalStyle::bg_color((40,40,40))
+                                } else {
+                                    TerminalStyle::default()
+                                }
+                            )
+                })
+        }        
+    }
+}
+
diff --git a/nested/src/product/mod.rs b/nested/src/product/mod.rs
new file mode 100644
index 0000000..1e44f4b
--- /dev/null
+++ b/nested/src/product/mod.rs
@@ -0,0 +1,4 @@
+
+pub mod editor;
+pub mod element;
+
diff --git a/nested/src/singleton/to_index.rs b/nested/src/singleton/to_index.rs
index 85e919f..da39758 100644
--- a/nested/src/singleton/to_index.rs
+++ b/nested/src/singleton/to_index.rs
@@ -82,10 +82,10 @@ where
 {
     fn reset(&mut self, view: Option<Arc<SrcView>>) {
         self.src_view = view;
-        self.cast.notify(&IndexArea::Full);
+        self.cast.notify(&IndexArea::Set(vec![ () ]));
     }
 
     fn notify(&mut self, _: &()) {
-        self.cast.notify(&IndexArea::Full);
+        self.cast.notify(&IndexArea::Set(vec![ () ]));
     }
 }
diff --git a/nested/src/string_editor.rs b/nested/src/string_editor.rs
index 74f0599..a6e00e4 100644
--- a/nested/src/string_editor.rs
+++ b/nested/src/string_editor.rs
@@ -2,12 +2,12 @@ use {
     crate::{
         core::{OuterViewPort, ViewPort},
         list::{sexpr::ListDecoration, ListEditor},
-        sequence::SequenceView,
+        sequence::{SequenceView, SequenceViewExt},
         singleton::{SingletonBuffer, SingletonView},
         terminal::{
             TerminalEditor, TerminalEditorResult, TerminalEvent, TerminalStyle, TerminalView,
         },
-        tree_nav::{TreeCursor, TreeNav, TreeNavResult},
+        tree_nav::{TreeCursor, TreeNav, TreeNavResult, TerminalTreeEditor},
     },
     std::sync::Arc,
     std::sync::RwLock,
@@ -62,6 +62,8 @@ impl TerminalEditor for CharEditor {
     }
 }
 
+impl TerminalTreeEditor for CharEditor {}
+
 //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
 
 pub struct StringEditor {
@@ -82,7 +84,18 @@ impl StringEditor {
     pub fn get_data_port(&self) -> OuterViewPort<dyn SequenceView<Item = char>> {
         self.chars_editor
             .get_data_port()
-            .map(|char_editor| char_editor.read().unwrap().data.get().unwrap())
+            .map(|char_editor| char_editor.read().unwrap().data.get().unwrap_or('?'))
+    }
+
+    pub fn get_plain_editor_view(&self) -> OuterViewPort<dyn TerminalView> {
+        self.chars_editor
+            .get_seg_seq_view()
+            .to_grid_horizontal()
+            .flatten()
+    }
+    
+    pub fn get_string(&self) -> String {
+        self.get_data_port().get_view().unwrap().iter().collect()
     }
 }
 
@@ -109,7 +122,7 @@ impl TreeNav for StringEditor {
         self.chars_editor.up()
     }
     fn dn(&mut self) -> TreeNavResult {
-        TreeNavResult::Exit
+        self.chars_editor.dn()
     }
 }
 
@@ -133,3 +146,6 @@ impl TerminalEditor for StringEditor {
         }
     }
 }
+
+impl TerminalTreeEditor for StringEditor {}
+
diff --git a/nested/src/tree_nav.rs b/nested/src/tree_nav.rs
index 757a1fb..800a82e 100644
--- a/nested/src/tree_nav.rs
+++ b/nested/src/tree_nav.rs
@@ -5,6 +5,7 @@ pub enum TreeNavResult {
     Continue,
     Exit,
 }
+
 /*
 impl From<TreeNavResult> for TerminalEditorResult {
     fn from(v: TreeNavResult) -> TerminalEditorResult {
@@ -14,13 +15,23 @@ impl From<TreeNavResult> for TerminalEditorResult {
         }
     }
 }
-*/
+ */
+
 #[derive(Clone, Eq, PartialEq)]
 pub struct TreeCursor {
     pub leaf_mode: ListCursorMode,
     pub tree_addr: Vec<usize>,
 }
 
+impl TreeCursor {
+    pub fn home() -> Self {
+        TreeCursor {
+            leaf_mode: ListCursorMode::Select,
+            tree_addr: vec![0]
+        }
+    }
+}
+
 impl Default for TreeCursor {
     fn default() -> Self {
         TreeCursor {
@@ -66,4 +77,5 @@ pub trait TreeNav {
 
 use crate::terminal::{TerminalEditor};
 
-pub trait TerminalTreeEditor = TerminalEditor + TreeNav;
+pub trait TerminalTreeEditor : TerminalEditor + TreeNav + Send {}
+
diff --git a/nested/src/vec/mod.rs b/nested/src/vec/mod.rs
index 3b081b5..2253a74 100644
--- a/nested/src/vec/mod.rs
+++ b/nested/src/vec/mod.rs
@@ -3,7 +3,7 @@ pub mod vec2bin;
 pub mod vec2json;
 pub mod vec2seq;
 
-pub use buffer::VecBuffer;
+pub use buffer::{VecBuffer, MutableVecAccess};
 
 //<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>