diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index 193a42f8e4..0bb62d6424 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -81,8 +81,6 @@ impl Symbol { ) }); - dbg!(ident_ids); - ident_ids .get_name(self.ident_id()) .unwrap_or_else(|| { @@ -576,7 +574,7 @@ impl IdentIds { if let Some(vec_elt) = by_id.get_mut(key_index) { *vec_elt = new_ident_name.into(); } else { - // we get the index from by_id so unless there is a bug in the rust std lib, this is unreachable + // we get the index from by_id unreachable!() } @@ -592,7 +590,7 @@ impl IdentIds { } } None => { - Err("Tried to update key in IdentIds but I could not find the key.".to_string()) + Err(format!("Tried to update key in IdentIds ({:?}) but I could not find the key ({}).", self.by_ident, old_ident_name)) } } } diff --git a/editor/src/editor/ed_error.rs b/editor/src/editor/ed_error.rs index 236627a6c3..6d997c7eb3 100644 --- a/editor/src/editor/ed_error.rs +++ b/editor/src/editor/ed_error.rs @@ -13,6 +13,15 @@ use snafu::{Backtrace, ErrorCompat, NoneError, ResultExt, Snafu}; #[snafu(visibility(pub))] pub enum EdError { + #[snafu(display( + "ASTNodeIdWithoutDefId: The expr_id_opt in ASTNode({:?}) was `None` but I was expexting `Some(DefId)` .", + ast_node_id + ))] + ASTNodeIdWithoutDefId { + ast_node_id: ASTNodeId, + backtrace: Backtrace, + }, + #[snafu(display( "ASTNodeIdWithoutExprId: The expr_id_opt in ASTNode({:?}) was `None` but I was expexting `Some(ExprId)` .", ast_node_id diff --git a/editor/src/editor/grid_node_map.rs b/editor/src/editor/grid_node_map.rs index 3faf7727d0..f3d485b7a5 100644 --- a/editor/src/editor/grid_node_map.rs +++ b/editor/src/editor/grid_node_map.rs @@ -148,8 +148,8 @@ impl GridNodeMap { } } - // returns start and end pos of Expr2, relevant AST node and MarkNodeId of the corresponding MarkupNode - pub fn get_expr_start_end_pos( + // returns start and end pos of Expr2/Def2, relevant AST node and MarkNodeId of the corresponding MarkupNode + pub fn get_block_start_end_pos( &self, caret_pos: TextPos, ed_model: &EdModel, diff --git a/editor/src/editor/markup/nodes.rs b/editor/src/editor/markup/nodes.rs index 39a1a7205f..98f79ed6dd 100644 --- a/editor/src/editor/markup/nodes.rs +++ b/editor/src/editor/markup/nodes.rs @@ -3,6 +3,7 @@ use crate::editor::ed_error::EdResult; use crate::editor::ed_error::ExpectedTextNode; use crate::editor::ed_error::{NestedNodeMissingChild, NestedNodeRequired}; use crate::editor::markup::common_nodes::new_blank_mn; +use crate::editor::markup::common_nodes::new_blank_mn_w_nl; use crate::editor::markup::common_nodes::new_colon_mn; use crate::editor::markup::common_nodes::new_comma_mn; use crate::editor::markup::common_nodes::new_equals_mn; @@ -19,7 +20,7 @@ use crate::lang::ast::DefId; use crate::lang::ast::ExprId; use crate::lang::ast::RecordField; use crate::lang::ast::ValueDef; -use crate::lang::ast::expr2_to_string; +use crate::editor::mvc::tld_value_update::tld_mark_node; use crate::lang::parse::ASTNodeId; use crate::lang::parse::{AppHeader, AST}; use crate::lang::pattern::get_identifier_string; @@ -284,7 +285,39 @@ pub fn def2_to_markup<'a, 'b>( mark_node_pool: &mut SlowPool, interns: &Interns, ) -> EdResult { - unimplemented!(); + + let ast_node_id = ASTNodeId::ADefId(def2_node_id); + + let mark_node_id = match def2 { + Def2::ValueDef { identifier_id, expr_id } => { + + let expr_mn_id = expr2_to_markup( + arena, + env, + env.pool.get(*expr_id), + *expr_id, + mark_node_pool, + interns, + )?; + + let expr_mn = mark_node_pool.get_mut(expr_mn_id); + expr_mn.add_newline_at_end(); + + + let tld_mn = tld_mark_node(*identifier_id, expr_mn_id, ast_node_id, mark_node_pool, env, interns)?; + + mark_node_pool.add( + tld_mn + ) + }, + Def2::Blank => { + mark_node_pool.add( + new_blank_mn_w_nl(ast_node_id, None) + ) + }, + }; + + Ok(mark_node_id) } // make Markup Nodes: generate String representation, assign Highlighting Style @@ -296,7 +329,6 @@ pub fn expr2_to_markup<'a, 'b>( mark_node_pool: &mut SlowPool, interns: &Interns, ) -> EdResult { - dbg!(expr2_to_string(expr2_node_id, env.pool)); let ast_node_id = ASTNodeId::AExprId(expr2_node_id); @@ -442,17 +474,14 @@ pub fn expr2_to_markup<'a, 'b>( Expr2::Blank => mark_node_pool.add(new_blank_mn(ast_node_id, None)), Expr2::LetValue { def_id, - body_id, - body_var, + body_id:_, + body_var:_, } => { - /*dbg!(expr2); - dbg!(env.pool.get(*body_id)); - dbg!(env.pool.get(*def_id)); - dbg!(body_var);*/ + let pattern_id = env.pool.get(*def_id).get_pattern_id(); let pattern2 = env.pool.get(pattern_id); - dbg!(pattern2); + let val_name = get_identifier_string(pattern2, interns)?; let val_name_mn = MarkupNode::Text { diff --git a/editor/src/editor/mvc/ed_update.rs b/editor/src/editor/mvc/ed_update.rs index c5990fef3a..45783776c9 100644 --- a/editor/src/editor/mvc/ed_update.rs +++ b/editor/src/editor/mvc/ed_update.rs @@ -8,6 +8,7 @@ use crate::editor::grid_node_map::GridNodeMap; use crate::editor::markup::attribute::Attributes; use crate::editor::markup::common_nodes::new_blank_mn; use crate::editor::markup::nodes; +use crate::editor::markup::nodes::EQUALS; use crate::editor::markup::nodes::MarkupNode; use crate::editor::mvc::app_update::InputOutcome; use crate::editor::mvc::ed_model::EdModel; @@ -26,6 +27,7 @@ use crate::editor::mvc::string_update::update_string; use crate::editor::slow_pool::MarkNodeId; use crate::editor::slow_pool::SlowPool; use crate::editor::syntax_highlight::HighlightStyle; +use crate::editor::mvc::tld_value_update::{start_new_tld_value, update_tld_val_name}; use crate::lang::ast::Def2; use crate::lang::ast::{Expr2, ExprId}; use crate::lang::constrain::constrain_expr; @@ -85,6 +87,18 @@ impl<'a> EdModel<'a> { } } + // disregards EdModel.code_lines because the caller knows the resulting caret position will be valid. + // allows us to prevent multiple updates to EdModel.code_lines + // TODO error if no match was found for old_caret_pos + pub fn simple_move_caret_right(&mut self, old_caret_pos: TextPos, repeat: usize) { + for caret_tup in self.caret_w_select_vec.iter_mut() { + if caret_tup.0.caret_pos == old_caret_pos { + caret_tup.0.caret_pos.column += 1; + caret_tup.1 = None; + } + } + } + // disregards EdModel.code_lines because the caller knows the resulting caret position will be valid. // allows us to prevent multiple updates to EdModel.code_lines pub fn simple_move_carets_left(&mut self, repeat: usize) { @@ -106,6 +120,7 @@ impl<'a> EdModel<'a> { // disregards EdModel.code_lines because the caller knows the resulting caret position will be valid. // allows us to prevent multiple updates to EdModel.code_lines + // TODO error if no match was found for old_caret_pos pub fn simple_move_caret_down(&mut self, old_caret_pos: TextPos, repeat: usize) { for caret_tup in self.caret_w_select_vec.iter_mut() { if caret_tup.0.caret_pos == old_caret_pos { @@ -311,7 +326,7 @@ impl<'a> EdModel<'a> { if self.grid_node_map.node_exists_at_pos(caret_pos) { let (expr_start_pos, expr_end_pos, ast_node_id, mark_node_id) = self .grid_node_map - .get_expr_start_end_pos(self.get_caret(), self)?; + .get_block_start_end_pos(self.get_caret(), self)?; self.set_selected_expr(expr_start_pos, expr_end_pos, ast_node_id, mark_node_id)?; } else if self @@ -320,7 +335,7 @@ impl<'a> EdModel<'a> { { let (expr_start_pos, expr_end_pos, ast_node_id, mark_node_id) = self .grid_node_map - .get_expr_start_end_pos(self.get_caret().decrement_col(), self)?; + .get_block_start_end_pos(self.get_caret().decrement_col(), self)?; self.set_selected_expr(expr_start_pos, expr_end_pos, ast_node_id, mark_node_id)?; } @@ -686,10 +701,37 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult let ast_node_id = curr_mark_node.get_ast_node_id(); match ast_node_id { - ASTNodeId::ADefId(_) => { - //let def_ref = ed_model.module.env.pool.get(def_id); + ASTNodeId::ADefId(def_id) => { + let def_ref = ed_model.module.env.pool.get(def_id); - unimplemented!("TODO") + match def_ref { + Def2::Blank {..} => { + match ch { + 'a'..='z' => { + start_new_tld_value(ed_model, ch)? + }, + _ => InputOutcome::Ignored + } + } + Def2::ValueDef { .. } => { + let val_name_mn_id = if curr_mark_node.get_content() == EQUALS { + if let Some(prev_mark_node_id) = prev_mark_node_id_opt { + prev_mark_node_id + } else { + unreachable!() + } + } else { + curr_mark_node_id + }; + + update_tld_val_name( + val_name_mn_id, + ed_model.get_caret(), // TODO update for multiple carets + ed_model, + ch + )? + }, + } }, ASTNodeId::AExprId(expr_id) => { let expr_ref = ed_model.module.env.pool.get(expr_id); diff --git a/editor/src/editor/mvc/let_update.rs b/editor/src/editor/mvc/let_update.rs index c97d2cbca2..3307ef836e 100644 --- a/editor/src/editor/mvc/let_update.rs +++ b/editor/src/editor/mvc/let_update.rs @@ -128,7 +128,7 @@ pub fn update_let_value( .grid_node_map .get_offset_to_node_id(old_caret_pos, val_name_mn_id)?; - if node_caret_offset != 0 && node_caret_offset <= content_str_mut.len() { + if node_caret_offset <= content_str_mut.len() { content_str_mut.insert(node_caret_offset, *new_char); // update ast diff --git a/editor/src/editor/mvc/mod.rs b/editor/src/editor/mvc/mod.rs index 7f77d10fee..b235051537 100644 --- a/editor/src/editor/mvc/mod.rs +++ b/editor/src/editor/mvc/mod.rs @@ -5,6 +5,7 @@ pub mod ed_update; pub mod ed_view; mod int_update; mod let_update; +pub mod tld_value_update; mod list_update; mod lookup_update; mod record_update; diff --git a/editor/src/editor/mvc/tld_value_update.rs b/editor/src/editor/mvc/tld_value_update.rs new file mode 100644 index 0000000000..d8296849f0 --- /dev/null +++ b/editor/src/editor/mvc/tld_value_update.rs @@ -0,0 +1,165 @@ +use roc_module::symbol::{Interns, Symbol}; + +use crate::{editor::{ed_error::{EdResult, KeyNotFound}, markup::{attribute::Attributes, common_nodes::{new_blank_mn_w_nl, new_equals_mn}, nodes::MarkupNode}, slow_pool::{MarkNodeId, SlowPool}, syntax_highlight::HighlightStyle}, lang::{ast::{Def2, Expr2}, expr::Env, parse::ASTNodeId, pattern::{Pattern2, get_identifier_string}, pool::NodeId}, ui::text::text_pos::TextPos}; + +use super::{app_update::InputOutcome, ed_model::EdModel, ed_update::{NodeContext, get_node_context}}; + + + +// Top Level Defined Value. example: `main = "Hello, World!"` + +pub fn tld_mark_node<'a>( + identifier_id: NodeId, + expr_mark_node_id: MarkNodeId, + ast_node_id: ASTNodeId, + mark_node_pool: &mut SlowPool, + env: &Env<'a>, + interns: &Interns, +) -> EdResult { + let pattern2 = env.pool.get(identifier_id); + let val_name = get_identifier_string(pattern2, interns)?; + + let val_name_mn = MarkupNode::Text { + content: val_name, + ast_node_id, + syn_high_style: HighlightStyle::Variable, + attributes: Attributes::new(), + parent_id_opt: None, + newline_at_end: false, + }; + + let val_name_mn_id = mark_node_pool.add(val_name_mn); + + let equals_mn_id = mark_node_pool.add(new_equals_mn(ast_node_id, None)); + + let expr_mn = mark_node_pool.get_mut(expr_mark_node_id); + expr_mn.add_newline_at_end(); + + let full_let_node = MarkupNode::Nested { + ast_node_id, + children_ids: vec![val_name_mn_id, equals_mn_id, expr_mark_node_id], + parent_id_opt: None, + newline_at_end: true, + }; + + Ok( + full_let_node + ) +} + +pub fn start_new_tld_value(ed_model: &mut EdModel, new_char: &char) -> 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 val_expr_node = Expr2::Blank; + let val_expr_id = ed_model.module.env.pool.add(val_expr_node); + + let val_expr_mn = new_blank_mn_w_nl(ASTNodeId::AExprId(val_expr_id), None); + let val_expr_mn_id = ed_model.mark_node_pool.add(val_expr_mn); + + let val_name_string = new_char.to_string(); + + let ident_id = ed_model + .module + .env + .ident_ids + .add(val_name_string.clone().into()); + + let module_ident_ids_opt = ed_model.loaded_module.interns.all_ident_ids.get_mut(&ed_model.module.env.home); + + if let Some(module_ident_ids_ref) = module_ident_ids_opt { + // this might create different IdentId for interns and env.ident_ids which may be a problem + module_ident_ids_ref.add(val_name_string.clone().into()); + } else { + KeyNotFound { + key_str: format!("{:?}", ed_model.module.env.home) + }.fail()? + } + + let val_symbol = Symbol::new(ed_model.module.env.home, ident_id); + + let patt2 = Pattern2::Identifier(val_symbol); + let patt2_id = ed_model.module.env.pool.add(patt2); + + let tld_mark_node = tld_mark_node( + patt2_id, + val_expr_mn_id, + ast_node_id, + &mut ed_model.mark_node_pool, + &ed_model.module.env, + &ed_model.loaded_module.interns + )?; + + let new_ast_node = + Def2::ValueDef { + identifier_id: patt2_id, + expr_id: val_expr_id, + }; + + ed_model.module.env.pool.set(ast_node_id.to_def_id()?, new_ast_node); + + ed_model + .mark_node_pool + .replace_node(curr_mark_node_id, tld_mark_node); + + // remove data corresponding to old Blank node + ed_model.del_at_line(old_caret_pos.line, old_caret_pos.column)?; + + let char_len = 1; + ed_model.simple_move_carets_right(char_len); + + ed_model.insert_all_between_line( + old_caret_pos.line, + old_caret_pos.column, + &ed_model.mark_node_pool.get(curr_mark_node_id).get_children_ids(), + )?; + + Ok(InputOutcome::Accepted) +} + +pub fn update_tld_val_name(val_name_mn_id: MarkNodeId, old_caret_pos: TextPos, ed_model: &mut EdModel, new_char: &char) -> EdResult { + if new_char.is_ascii_alphanumeric() { + // update markup + let val_name_mn_mut = ed_model.mark_node_pool.get_mut(val_name_mn_id); + let content_str_mut = val_name_mn_mut.get_content_mut()?; + + let old_val_name = content_str_mut.clone(); + + let node_caret_offset = ed_model + .grid_node_map + .get_offset_to_node_id(old_caret_pos, val_name_mn_id)?; + + if node_caret_offset <= content_str_mut.len() { + content_str_mut.insert(node_caret_offset, *new_char); + + // TODO no unwrap + ed_model + .module + .env + .ident_ids + .update_key(&old_val_name, content_str_mut) + .unwrap(); + + ed_model.insert_between_line( + old_caret_pos.line, + old_caret_pos.column, + &new_char.to_string(), + val_name_mn_id, + )?; + + ed_model.simple_move_caret_right(old_caret_pos, 1); + + Ok(InputOutcome::Accepted) + + } else { + Ok(InputOutcome::Ignored) + } + } else { + Ok(InputOutcome::Ignored) + } +} \ No newline at end of file diff --git a/editor/src/lang/expr.rs b/editor/src/lang/expr.rs index e04b69cb39..777d142def 100644 --- a/editor/src/lang/expr.rs +++ b/editor/src/lang/expr.rs @@ -1017,14 +1017,12 @@ pub fn to_def2_from_def<'a>( Body(&loc_pattern, &loc_expr) => { // TODO loc_pattern use identifier let expr2 = loc_expr_to_expr2(arena, loc_expr, env, scope, region).0; - dbg!(&expr2); let expr_id = env.pool.add(expr2); - dbg!(expr_id); use roc_parse::ast::Pattern::*; match loc_pattern.value { - Identifier(str_ref) => { + Identifier(_) => { let (_, pattern2) = to_pattern2( env, scope, diff --git a/editor/src/lang/parse.rs b/editor/src/lang/parse.rs index 50be3020e0..5eb3dd7089 100644 --- a/editor/src/lang/parse.rs +++ b/editor/src/lang/parse.rs @@ -32,6 +32,19 @@ impl ASTNodeId { } } } + + pub fn to_def_id(&self) -> EdResult{ + match self { + ASTNodeId::ADefId(def_id) => { + Ok(*def_id) + }, + _ => { + ASTNodeIdWithoutExprId { + ast_node_id: *self + }.fail()? + } + } + } } #[derive(Debug)]