From 786d07eb3e03a81e1747f732527e638b03513b9e Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 5 May 2024 19:48:31 -0400 Subject: [PATCH] feat: Navigate to convenient editing loc after insert; auto fill for node creation --- src/engine/command.rs | 3 +++ src/engine/doc.rs | 3 +++ src/language/interface.rs | 21 +++++++++++++++++++++ src/runtime.rs | 5 +++-- src/tree/location.rs | 21 +++++++++++++++++++++ src/tree/node.rs | 29 +++++++++++++++++++---------- 6 files changed, 70 insertions(+), 12 deletions(-) diff --git a/src/engine/command.rs b/src/engine/command.rs index 93c3c4b..3f8dbde 100644 --- a/src/engine/command.rs +++ b/src/engine/command.rs @@ -90,6 +90,9 @@ pub enum TreeNavCommand { /// If the node before the cursor is texty, enter text mode, placing the cursor at the /// end of the text. EnterText, + /// Use this when the node before the cursor has just been `Insert`ed, to move the cursor to a + /// convenient editing location. + FirstInsertLoc, } #[derive(Debug)] diff --git a/src/engine/doc.rs b/src/engine/doc.rs index 0b9c3b2..daacc51 100644 --- a/src/engine/doc.rs +++ b/src/engine/doc.rs @@ -398,6 +398,9 @@ fn execute_tree_nav( EnterText => cursor .left_node(s) .and_then(|node| Location::end_of_text(s, node)), + FirstInsertLoc => cursor + .left_node(s) + .map(|node| Location::first_insert_loc(s, node)), }; if let Some(new_loc) = new_loc { diff --git a/src/language/interface.rs b/src/language/interface.rs index de7497b..8d54492 100644 --- a/src/language/interface.rs +++ b/src/language/interface.rs @@ -252,6 +252,23 @@ impl Sort { construct: id, }) } + + /// Returns the single unique construct in this sort, or `None` if it has zero or more than one + /// construct. + pub fn unique_construct(self, s: &Storage) -> Option { + let mut unique = None; + for construct in self.matching_constructs(s) { + if construct.is_hole(s) { + continue; + } + if unique.is_some() { + return None; + } else { + unique = Some(construct); + } + } + unique + } } impl Construct { @@ -295,6 +312,10 @@ impl Construct { grammar(s, self.language).constructs[self.construct].is_comment_or_ws } + pub fn is_hole(self, s: &Storage) -> bool { + grammar(s, self.language).hole_construct == self.construct + } + pub fn is_root(self, s: &Storage) -> bool { grammar(s, self.language).root_construct == self.construct } diff --git a/src/runtime.rs b/src/runtime.rs index c0b1ac3..1ff9613 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -348,8 +348,9 @@ impl + 'static> Runtime { } pub fn insert_node(&mut self, construct: Construct) -> Result<(), SynlessError> { - let node = Node::new(self.engine.raw_storage_mut(), construct); - self.engine.execute(TreeEdCommand::Insert(node)) + let node = Node::new_with_auto_fill(self.engine.raw_storage_mut(), construct); + self.engine.execute(TreeEdCommand::Insert(node))?; + self.engine.execute(TreeNavCommand::FirstInsertLoc) } /************* diff --git a/src/tree/location.rs b/src/tree/location.rs index 67762e9..0597ab4 100644 --- a/src/tree/location.rs +++ b/src/tree/location.rs @@ -89,6 +89,27 @@ impl Location { Some(Location(LocationInner::InText(node, text_len))) } + /// Where to move the cursor after inserting this node. + pub fn first_insert_loc(s: &Storage, node: Node) -> Location { + Location::first_insert_loc_impl(s, node, true) + } + + fn first_insert_loc_impl(s: &Storage, node: Node, top_level: bool) -> Location { + match node.arity(s) { + Arity::Texty => Location::end_of_text(s, node).bug(), + Arity::Listy(_) => Location::before_children(s, node).bug(), + Arity::Fixed(_) => { + if let Some(child) = node.first_child(s) { + Location::first_insert_loc_impl(s, child, false) + } else if top_level { + Location::after(s, node) + } else { + Location::before(s, node) + } + } + } + } + /************* * Accessors * *************/ diff --git a/src/tree/node.rs b/src/tree/node.rs index 11f3165..797020f 100644 --- a/src/tree/node.rs +++ b/src/tree/node.rs @@ -50,11 +50,20 @@ impl Node { ****************/ pub fn new_hole(s: &mut Storage, lang: Language) -> Node { - Node::new(s, lang.hole_construct(s)) + Node::new_impl(s, lang.hole_construct(s), false) } /// Creates a new root node. pub fn new(s: &mut Storage, construct: Construct) -> Node { + Node::new_impl(s, construct, false) + } + + /// Creates a new root node, filling in any children that can only be one construct. + pub fn new_with_auto_fill(s: &mut Storage, construct: Construct) -> Node { + Node::new_impl(s, construct, true) + } + + fn new_impl(s: &mut Storage, construct: Construct, auto_fill: bool) -> Node { let id = inc_id(&mut s.node_forest.next_id); match construct.arity(s) { Arity::Texty => Node(s.forest_mut().new_node(NodeData { @@ -73,16 +82,16 @@ impl Node { construct, text: None, }); - let num_children = sorts.len(s); let hole_construct = construct.language().hole_construct(s); - for _ in 0..num_children { - let child_id = inc_id(&mut s.node_forest.next_id); - let child = s.forest_mut().new_node(NodeData { - id: child_id, - construct: hole_construct, - text: None, - }); - bug_assert!(s.forest_mut().insert_last_child(parent, child)); + for i in 0..sorts.len(s) { + let sort = sorts.get(s, i).bug(); + let child = match sort.unique_construct(s) { + Some(child_construct) if auto_fill => { + Node::new_impl(s, child_construct, auto_fill) + } + _ => Node::new_impl(s, hole_construct, false), + }; + bug_assert!(s.forest_mut().insert_last_child(parent, child.0)); } Node(parent) }