diff --git a/editor/src/editor/ed_error.rs b/editor/src/editor/ed_error.rs index 588169d61a..6b4745eb6e 100644 --- a/editor/src/editor/ed_error.rs +++ b/editor/src/editor/ed_error.rs @@ -62,6 +62,15 @@ pub enum EdError { backtrace: Backtrace, }, + #[snafu(display( + "MissingParent: MarkupNode with id {} should have a parent but there was none.", + node_id + ))] + MissingParent { + node_id: MarkNodeId, + backtrace: Backtrace, + }, + #[snafu(display("NestedNodeMissingChild: expected to find child with id {} in Nested MarkupNode, but it was missing. Id's of the children are {:?}.", node_id, children_ids))] NestedNodeMissingChild { node_id: MarkNodeId, diff --git a/editor/src/editor/mvc/ed_update.rs b/editor/src/editor/mvc/ed_update.rs index 7a527a412f..f6e02aa353 100644 --- a/editor/src/editor/mvc/ed_update.rs +++ b/editor/src/editor/mvc/ed_update.rs @@ -1,11 +1,13 @@ use crate::editor::code_lines::CodeLines; use crate::editor::ed_error::EdResult; -use crate::editor::ed_error::RecordWithoutFields; use crate::editor::grid_node_map::GridNodeMap; use crate::editor::markup::attribute::Attributes; use crate::editor::markup::nodes; use crate::editor::markup::nodes::MarkupNode; use crate::editor::mvc::ed_model::EdModel; +use crate::editor::mvc::record_update::start_new_record; +use crate::editor::mvc::record_update::update_record_colon; +use crate::editor::mvc::record_update::update_record_field; use crate::editor::slow_pool::MarkNodeId; use crate::editor::slow_pool::SlowPool; use crate::editor::syntax_highlight::HighlightStyle; @@ -22,8 +24,6 @@ use crate::ui::text::{lines, lines::Lines, lines::SelectableLines}; use crate::ui::ui_error::UIResult; use crate::ui::util::is_newline; use crate::window::keyboard_input::Modifiers; -use roc_types::subs::Variable; -use snafu::OptionExt; use winit::event::VirtualKeyCode; use VirtualKeyCode::*; @@ -265,157 +265,43 @@ impl<'a> SelectableLines for EdModel<'a> { } } +pub struct NodeContext<'a> { + pub old_caret_pos: TextPos, + pub curr_mark_node_id: MarkNodeId, + pub curr_mark_node: &'a MarkupNode, + pub parent_id_opt: Option, + pub ast_node_id: NodeId, +} + +pub fn get_node_context<'a>(ed_model: &'a EdModel) -> EdResult> { + let old_caret_pos = ed_model.get_caret(); + let curr_mark_node_id = ed_model + .grid_node_map + .get_id_at_row_col(ed_model.get_caret())?; + let curr_mark_node = ed_model.markup_node_pool.get(curr_mark_node_id); + let parent_id_opt = curr_mark_node.get_parent_id_opt(); + let ast_node_id = curr_mark_node.get_ast_node_id(); + + Ok(NodeContext { + old_caret_pos, + curr_mark_node_id, + curr_mark_node, + parent_id_opt, + ast_node_id, + }) +} + pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult<()> { // TODO set all selections to none // TODO nested records match received_char { '{' => { - let old_caret_pos = ed_model.get_caret(); - - let curr_mark_node_id = ed_model.grid_node_map.get_id_at_row_col(old_caret_pos)?; - let curr_mark_node = ed_model.markup_node_pool.get(curr_mark_node_id); - let is_blank_node = curr_mark_node.is_blank(); - let parent_id_opt = curr_mark_node.get_parent_id_opt(); - let ast_node_id = curr_mark_node.get_ast_node_id(); - - let ast_pool = &mut ed_model.module.env.pool; - let expr2_node = Expr2::EmptyRecord; - - let mark_node_pool = &mut ed_model.markup_node_pool; - - ast_pool.set(ast_node_id, expr2_node); - - let left_bracket_node = MarkupNode::Text { - content: nodes::LEFT_ACCOLADE.to_owned(), - ast_node_id, - syn_high_style: HighlightStyle::Bracket, - attributes: Attributes::new(), - parent_id_opt: Some(curr_mark_node_id), - }; - - let left_bracket_node_id = mark_node_pool.add(left_bracket_node); - - let right_bracket_node = MarkupNode::Text { - content: nodes::RIGHT_ACCOLADE.to_owned(), - ast_node_id, - syn_high_style: HighlightStyle::Bracket, - attributes: Attributes::new(), - parent_id_opt: Some(curr_mark_node_id), - }; - - let right_bracket_node_id = mark_node_pool.add(right_bracket_node); - - let nested_node = MarkupNode::Nested { - ast_node_id, - children_ids: vec![left_bracket_node_id, right_bracket_node_id], - parent_id_opt, - }; - - if is_blank_node { - mark_node_pool.replace_node(curr_mark_node_id, nested_node); - - for _ in 0..nodes::LEFT_ACCOLADE.len() { - ed_model.simple_move_carets_right(); - } - - // update GridNodeMap - ed_model.grid_node_map.add_to_line( - old_caret_pos.line, - nodes::LEFT_ACCOLADE.len(), - left_bracket_node_id - )?; - - ed_model.grid_node_map.add_to_line( - old_caret_pos.line, - nodes::RIGHT_ACCOLADE.len(), - right_bracket_node_id - )?; - } + start_new_record(ed_model)?; } ':' => { - let old_caret_pos = ed_model.get_caret(); - - let curr_mark_node_id = ed_model.grid_node_map.get_id_at_row_col(old_caret_pos)?; - let curr_mark_node = ed_model.markup_node_pool.get(curr_mark_node_id); - let parent_id_opt = curr_mark_node.get_parent_id_opt(); - let ast_node_id = curr_mark_node.get_ast_node_id(); - - if let Some(parent_id) = parent_id_opt { - let sibling_ids = curr_mark_node.get_sibling_ids(&ed_model.markup_node_pool); - - let new_child_index = index_of(curr_mark_node_id, &sibling_ids)? + 1; - - let ast_node_ref = ed_model.module.env.pool.get(ast_node_id); - - match ast_node_ref { - Expr2::Record { record_var:_, fields } => { - // update Markup - let record_colon = nodes::COLON; - - let record_colon_node = MarkupNode::Text { - content: record_colon.to_owned(), - ast_node_id, - syn_high_style: HighlightStyle::Operator, - attributes: Attributes::new(), - parent_id_opt: Some(parent_id), - }; - - let record_colon_node_id = ed_model.markup_node_pool.add(record_colon_node); - ed_model - .markup_node_pool - .get_mut(parent_id) - .add_child_at_index(new_child_index, record_colon_node_id)?; - - - let record_blank_node = MarkupNode::Blank { - ast_node_id, - syn_high_style: HighlightStyle::Blank, - attributes: Attributes::new(), - parent_id_opt: Some(parent_id), - }; - - let record_blank_node_id = ed_model.markup_node_pool.add(record_blank_node); - ed_model - .markup_node_pool - .get_mut(parent_id) - .add_child_at_index(new_child_index + 1, record_blank_node_id)?; - - // update caret - for _ in 0..record_colon.len() { - ed_model.simple_move_carets_right(); - } - - // update GridNodeMap - ed_model.grid_node_map.add_to_line( - old_caret_pos.line, - nodes::COLON.len(), - record_colon_node_id, - )?; - - ed_model.grid_node_map.add_to_line( - old_caret_pos.line, - nodes::BLANK_PLACEHOLDER.len(), - record_blank_node_id, - )?; - - // update AST node - let new_field_val = Expr2::Blank; - let new_field_val_id = ed_model.module.env.pool.add(new_field_val); - - let first_field_mut = - fields - .iter_mut(ed_model.module.env.pool) - .next() - .with_context( - || RecordWithoutFields {} - )?; - - first_field_mut.2 = new_field_val_id; - } - other => unimplemented!("TODO implement updating of Expr2 {:?}.", other) - } - } + // TODO set up Dict if previous char is '{' + update_record_colon(ed_model)?; } '\u{8}' | '\u{7f}' => { // On Linux, '\u{8}' is backspace, @@ -437,11 +323,11 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult } ch => { let old_caret_pos = ed_model.get_caret(); - - let curr_mark_node_id = ed_model.grid_node_map.get_id_at_row_col(old_caret_pos)?; + let curr_mark_node_id = ed_model.grid_node_map.get_id_at_row_col(ed_model.get_caret())?; let curr_mark_node = ed_model.markup_node_pool.get(curr_mark_node_id); let parent_id_opt = curr_mark_node.get_parent_id_opt(); let ast_node_id = curr_mark_node.get_ast_node_id(); + let ast_node_ref = ed_model.module.env.pool.get(ast_node_id); if let Some(parent_id) = parent_id_opt { @@ -481,13 +367,12 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult )?; // update AST - let field_str = &ch.to_string(); let record_var = ed_model.module.env.var_store.fresh(); - let field_name = PoolStr::new(field_str, &mut ed_model.module.env.pool); + let field_name = PoolStr::new(record_field_str, &mut ed_model.module.env.pool); let field_var = ed_model.module.env.var_store.fresh(); //TODO actually check if field_str belongs to a previously defined variable let field_val = Expr2::InvalidLookup( - PoolStr::new(field_str, ed_model.module.env.pool) + PoolStr::new(record_field_str, ed_model.module.env.pool) ); let field_val_id = ed_model.module.env.pool.add(field_val); let first_field = (field_name, field_var, field_val_id); @@ -504,61 +389,19 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult }; ed_model.module.env.pool.set(ast_node_id, new_ast_node); + } else { + unimplemented!("TODO handle this else") } }, Expr2::Record { record_var:_, fields } => { - if new_child_index == 2 { - - // update MarkupNode - let record_field_str_add = &ch.to_string(); - - let curr_mark_node_mut = ed_model.markup_node_pool.get_mut(curr_mark_node_id); - let content_str_mut = curr_mark_node_mut.get_content_mut()?; - content_str_mut.push_str(record_field_str_add); - - // update caret - for _ in 0..record_field_str_add.len() { - ed_model.simple_move_carets_right(); - } - - // update GridNodeMap - ed_model.grid_node_map.insert_between_line( - old_caret_pos.line, - old_caret_pos.column, - record_field_str_add.len(), - curr_mark_node_id, - )?; - - // update AST Node - let first_field = - fields - .iter(ed_model.module.env.pool) - .next() - .with_context( - || RecordWithoutFields {} - )?; - - let mut new_field_name = String::new(); - - first_field.0.as_str(ed_model.module.env.pool).to_string(); - - - new_field_name.push(*ch); - - let new_pool_str = PoolStr::new(&new_field_name, &mut ed_model.module.env.pool); - - let first_field_mut = - fields - .iter_mut(ed_model.module.env.pool) - .next() - .with_context( - || RecordWithoutFields {} - )?; - - first_field_mut.0 = new_pool_str; - - // TODO adjust grid_node_map - } + update_record_field( + &ch.to_string(), + old_caret_pos, + curr_mark_node_id, + new_child_index, + fields, + ed_model, + )?; }, other => { unimplemented!("TODO implement updating of Expr2 {:?}.", other) @@ -570,73 +413,3 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult Ok(()) } - -/* - let old_caret_pos = ed_model.caret_pos; - - match received_char { - '\u{8}' | '\u{7f}' => { - // On Linux, '\u{8}' is backspace, - // on macOS '\u{7f}'. - if let Some(selection) = ed_model.selection_opt { - del_selection(selection, ed_model)?; - } else { - ed_model.caret_pos = - move_caret_left(old_caret_pos, None, false, &ed_model.text_buf).0; - - ed_model.text_buf.pop_char(old_caret_pos); - } - - ed_model.selection_opt = None; - } - ch if is_newline(ch) => { - if let Some(selection) = ed_model.selection_opt { - del_selection(selection, ed_model)?; - ed_model.text_buf.insert_char(ed_model.caret_pos, &'\n')?; - } else { - ed_model.text_buf.insert_char(old_caret_pos, &'\n')?; - - ed_model.caret_pos = Position { - line: old_caret_pos.line + 1, - column: 0, - }; - } - - ed_model.selection_opt = None; - } - '\u{1}' // Ctrl + A - | '\u{3}' // Ctrl + C - | '\u{16}' // Ctrl + V - | '\u{18}' // Ctrl + X - | '\u{e000}'..='\u{f8ff}' // http://www.unicode.org/faq/private_use.html - | '\u{f0000}'..='\u{ffffd}' // ^ - | '\u{100000}'..='\u{10fffd}' // ^ - => { - // chars that can be ignored - } - _ => { - if let Some(selection) = ed_model.selection_opt { - del_selection(selection, ed_model)?; - ed_model - .text_buf - .insert_char(ed_model.caret_pos, received_char)?; - - ed_model.caret_pos = - move_caret_right(ed_model.caret_pos, None, false, &ed_model.text_buf).0; - } else { - ed_model - .text_buf - .insert_char(old_caret_pos, received_char)?; - - ed_model.caret_pos = Position { - line: old_caret_pos.line, - column: old_caret_pos.column + 1, - }; - } - - ed_model.selection_opt = None; - } - } - - Ok(()) -*/ diff --git a/editor/src/editor/mvc/mod.rs b/editor/src/editor/mvc/mod.rs index af950775db..672fcdb6cf 100644 --- a/editor/src/editor/mvc/mod.rs +++ b/editor/src/editor/mvc/mod.rs @@ -3,3 +3,4 @@ pub mod app_update; pub mod ed_model; pub mod ed_update; pub mod ed_view; +mod record_update; diff --git a/editor/src/editor/mvc/record_update.rs b/editor/src/editor/mvc/record_update.rs new file mode 100644 index 0000000000..c8bab50b43 --- /dev/null +++ b/editor/src/editor/mvc/record_update.rs @@ -0,0 +1,233 @@ +use crate::editor::ed_error::EdResult; +use crate::editor::ed_error::MissingParent; +use crate::editor::ed_error::RecordWithoutFields; +use crate::editor::markup::attribute::Attributes; +use crate::editor::markup::nodes; +use crate::editor::markup::nodes::MarkupNode; +use crate::editor::mvc::ed_model::EdModel; +use crate::editor::mvc::ed_update::get_node_context; +use crate::editor::mvc::ed_update::NodeContext; +use crate::editor::slow_pool::MarkNodeId; +use crate::editor::syntax_highlight::HighlightStyle; +use crate::editor::util::index_of; +use crate::lang::ast::Expr2; +use crate::lang::pool::NodeId; +use crate::lang::pool::PoolStr; +use crate::lang::pool::PoolVec; +use crate::ui::text::text_pos::TextPos; +use roc_types::subs::Variable; +use snafu::OptionExt; + +pub fn start_new_record(ed_model: &mut EdModel) -> EdResult<()> { + let NodeContext { + old_caret_pos, + curr_mark_node_id, + curr_mark_node, + parent_id_opt, + ast_node_id, + } = get_node_context(&ed_model)?; + + let is_blank_node = curr_mark_node.is_blank(); + + let ast_pool = &mut ed_model.module.env.pool; + let expr2_node = Expr2::EmptyRecord; + + let mark_node_pool = &mut ed_model.markup_node_pool; + + ast_pool.set(ast_node_id, expr2_node); + + let left_bracket_node = MarkupNode::Text { + content: nodes::LEFT_ACCOLADE.to_owned(), + ast_node_id, + syn_high_style: HighlightStyle::Bracket, + attributes: Attributes::new(), + parent_id_opt: Some(curr_mark_node_id), + }; + + let left_bracket_node_id = mark_node_pool.add(left_bracket_node); + + let right_bracket_node = MarkupNode::Text { + content: nodes::RIGHT_ACCOLADE.to_owned(), + ast_node_id, + syn_high_style: HighlightStyle::Bracket, + attributes: Attributes::new(), + parent_id_opt: Some(curr_mark_node_id), + }; + + let right_bracket_node_id = mark_node_pool.add(right_bracket_node); + + let nested_node = MarkupNode::Nested { + ast_node_id, + children_ids: vec![left_bracket_node_id, right_bracket_node_id], + parent_id_opt, + }; + + if is_blank_node { + mark_node_pool.replace_node(curr_mark_node_id, nested_node); + + for _ in 0..nodes::LEFT_ACCOLADE.len() { + ed_model.simple_move_carets_right(); + } + + // update GridNodeMap + ed_model.grid_node_map.add_to_line( + old_caret_pos.line, + nodes::LEFT_ACCOLADE.len(), + left_bracket_node_id, + )?; + + ed_model.grid_node_map.add_to_line( + old_caret_pos.line, + nodes::RIGHT_ACCOLADE.len(), + right_bracket_node_id, + )?; + } + + Ok(()) +} + +pub fn update_record_field( + new_input: &str, + old_caret_pos: TextPos, + curr_mark_node_id: MarkNodeId, + new_child_index: usize, + record_fields: &PoolVec<(PoolStr, Variable, NodeId)>, + ed_model: &mut EdModel, +) -> EdResult<()> { + if new_child_index == 2 { + // update MarkupNode + let curr_mark_node_mut = ed_model.markup_node_pool.get_mut(curr_mark_node_id); + let content_str_mut = curr_mark_node_mut.get_content_mut()?; + content_str_mut.push_str(new_input); + + // update caret + for _ in 0..new_input.len() { + ed_model.simple_move_carets_right(); + } + + // update GridNodeMap + ed_model.grid_node_map.insert_between_line( + old_caret_pos.line, + old_caret_pos.column, + new_input.len(), + curr_mark_node_id, + )?; + + // update AST Node + let first_field = record_fields + .iter(ed_model.module.env.pool) + .next() + .with_context(|| RecordWithoutFields {})?; + + let mut new_field_name = String::new(); + + first_field.0.as_str(ed_model.module.env.pool).to_string(); + + new_field_name.push_str(new_input); + + let new_pool_str = PoolStr::new(&new_field_name, &mut ed_model.module.env.pool); + + let first_field_mut = record_fields + .iter_mut(ed_model.module.env.pool) + .next() + .with_context(|| RecordWithoutFields {})?; + + first_field_mut.0 = new_pool_str; + + Ok(()) + } else { + unimplemented!("TODO implement updating of other fields of record.") + } +} + +pub fn update_record_colon(ed_model: &mut EdModel) -> EdResult<()> { + let NodeContext { + old_caret_pos, + curr_mark_node_id, + curr_mark_node, + parent_id_opt, + ast_node_id, + } = get_node_context(&ed_model)?; + + if let Some(parent_id) = parent_id_opt { + let sibling_ids = curr_mark_node.get_sibling_ids(&ed_model.markup_node_pool); + + let new_child_index = index_of(curr_mark_node_id, &sibling_ids)? + 1; + + let ast_node_ref = ed_model.module.env.pool.get(ast_node_id); + + match ast_node_ref { + Expr2::Record { + record_var: _, + fields, + } => { + // update Markup + let record_colon = nodes::COLON; + + let record_colon_node = MarkupNode::Text { + content: record_colon.to_owned(), + ast_node_id, + syn_high_style: HighlightStyle::Operator, + attributes: Attributes::new(), + parent_id_opt: Some(parent_id), + }; + + let record_colon_node_id = ed_model.markup_node_pool.add(record_colon_node); + ed_model + .markup_node_pool + .get_mut(parent_id) + .add_child_at_index(new_child_index, record_colon_node_id)?; + + let record_blank_node = MarkupNode::Blank { + ast_node_id, + syn_high_style: HighlightStyle::Blank, + attributes: Attributes::new(), + parent_id_opt: Some(parent_id), + }; + + let record_blank_node_id = ed_model.markup_node_pool.add(record_blank_node); + ed_model + .markup_node_pool + .get_mut(parent_id) + .add_child_at_index(new_child_index + 1, record_blank_node_id)?; + + // update caret + for _ in 0..record_colon.len() { + ed_model.simple_move_carets_right(); + } + + // update GridNodeMap + ed_model.grid_node_map.add_to_line( + old_caret_pos.line, + nodes::COLON.len(), + record_colon_node_id, + )?; + + ed_model.grid_node_map.add_to_line( + old_caret_pos.line, + nodes::BLANK_PLACEHOLDER.len(), + record_blank_node_id, + )?; + + // update AST node + let new_field_val = Expr2::Blank; + let new_field_val_id = ed_model.module.env.pool.add(new_field_val); + + let first_field_mut = fields + .iter_mut(ed_model.module.env.pool) + .next() + .with_context(|| RecordWithoutFields {})?; + + first_field_mut.2 = new_field_val_id; + } + other => unimplemented!("TODO implement updating of Expr2 {:?}.", other), + } + + Ok(()) + } else { + MissingParent { + node_id: curr_mark_node_id, + } + .fail() + } +}