diff --git a/editor/src/editor/ed_error.rs b/editor/src/editor/ed_error.rs index 0f80a0398a..a21cb2de86 100644 --- a/editor/src/editor/ed_error.rs +++ b/editor/src/editor/ed_error.rs @@ -111,6 +111,26 @@ pub enum EdError { backtrace: Backtrace, }, + #[snafu(display( + "UnexpectedASTNode: required a {} at this position, node was a {}.", + required_node_type, + encountered_node_type + ))] + UnexpectedASTNode { + required_node_type: String, + encountered_node_type: String, + backtrace: Backtrace, + }, + + #[snafu(display( + "UnexpectedEmptyPoolVec: expected PoolVec {} to have at least one element.", + descriptive_vec_name + ))] + UnexpectedEmptyPoolVec { + descriptive_vec_name: String, + backtrace: Backtrace, + }, + #[snafu(display( "OutOfBounds: index {} was out of bounds for {} with length {}.", index, @@ -127,7 +147,7 @@ pub enum EdError { #[snafu(display("ParseError: Failed to parse AST: SyntaxError: {}.", syntax_err))] ParseError { syntax_err: String }, - #[snafu(display("RecordWithoutFields: expected record to have at least one field because it is not an EmpyRecord."))] + #[snafu(display("RecordWithoutFields: expected record to have at least one field because it is not an EmptyRecord."))] RecordWithoutFields { backtrace: Backtrace }, #[snafu(display("StringParseError: {}", msg))] diff --git a/editor/src/editor/main.rs b/editor/src/editor/main.rs index 352adc5818..94cdf006e1 100644 --- a/editor/src/editor/main.rs +++ b/editor/src/editor/main.rs @@ -26,6 +26,7 @@ use bumpalo::collections::String as BumpString; use bumpalo::Bump; use cgmath::Vector2; use pipelines::RectResources; +use roc_module::symbol::Interns; use roc_module::symbol::{IdentIds, ModuleIds}; use roc_types::subs::VarStore; use std::{error::Error, io, path::Path}; @@ -140,13 +141,18 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box> { let mut module_ids = ModuleIds::default(); let mod_id = module_ids.get_or_insert(&"ModId123".into()); + let interns = Interns { + module_ids, + all_ident_ids: IdentIds::exposed_builtins(8), + }; + let env = Env::new( mod_id, &env_arena, &mut env_pool, &mut var_store, dep_idents, - &module_ids, + &interns.module_ids, exposed_ident_ids, ); @@ -172,7 +178,7 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box> { }; let ed_model_opt = { - let ed_model_res = ed_model::init_model(&code_str, file_path, env, &code_arena); + let ed_model_res = ed_model::init_model(&code_str, file_path, env, &interns, &code_arena); match ed_model_res { Ok(mut ed_model) => { diff --git a/editor/src/editor/markup/nodes.rs b/editor/src/editor/markup/nodes.rs index 2d09f21ed6..8faf0cbf4b 100644 --- a/editor/src/editor/markup/nodes.rs +++ b/editor/src/editor/markup/nodes.rs @@ -143,6 +143,8 @@ fn get_string<'a>(env: &Env<'a>, pool_str: &PoolStr) -> String { pub const BLANK_PLACEHOLDER: &str = " "; pub const LEFT_ACCOLADE: &str = "{ "; pub const RIGHT_ACCOLADE: &str = " }"; +pub const LEFT_SQUARE_BR: &str = "[ "; +pub const RIGHT_SQUARE_BR: &str = " ]"; pub const COLON: &str = ": "; pub const STRING_QUOTES: &str = "\"\""; @@ -209,7 +211,7 @@ pub fn expr2_to_markup<'a, 'b>( } Expr2::List { elems, .. } => { let mut children_ids = vec![new_markup_node( - "[ ".to_string(), + LEFT_SQUARE_BR.to_string(), expr2_node_id, HighlightStyle::Bracket, markup_node_pool, @@ -236,7 +238,7 @@ pub fn expr2_to_markup<'a, 'b>( } } children_ids.push(new_markup_node( - "] ".to_string(), + RIGHT_SQUARE_BR.to_string(), expr2_node_id, HighlightStyle::Bracket, markup_node_pool, diff --git a/editor/src/editor/mvc/ed_model.rs b/editor/src/editor/mvc/ed_model.rs index 64b36ecf7e..3700163417 100644 --- a/editor/src/editor/mvc/ed_model.rs +++ b/editor/src/editor/mvc/ed_model.rs @@ -21,6 +21,7 @@ use crate::ui::ui_error::UIResult; use bumpalo::collections::String as BumpString; use bumpalo::Bump; use nonempty::NonEmpty; +use roc_module::symbol::Interns; use roc_region::all::Region; use std::path::Path; @@ -38,6 +39,7 @@ pub struct EdModel<'a> { pub has_focus: bool, pub caret_w_select_vec: NonEmpty<(CaretWSelect, Option)>, pub selected_expr_opt: Option, + pub interns: &'a Interns, // this should eventually come from LoadedModule, see #1442 pub show_debug_view: bool, // EdModel is dirty if it has changed since the previous render. pub dirty: bool, @@ -54,6 +56,7 @@ pub fn init_model<'a>( code_str: &'a BumpString, file_path: &'a Path, env: Env<'a>, + interns: &'a Interns, code_arena: &'a Bump, ) -> EdResult> { let mut module = EdModule::new(&code_str, env, code_arena)?; @@ -99,6 +102,7 @@ pub fn init_model<'a>( has_focus: true, caret_w_select_vec: NonEmpty::new((CaretWSelect::default(), None)), selected_expr_opt: None, + interns, show_debug_view: false, dirty: true, }) @@ -185,7 +189,7 @@ pub mod test_ed_model { use bumpalo::collections::String as BumpString; use bumpalo::Bump; use ed_model::EdModel; - use roc_module::symbol::{IdentIds, ModuleIds}; + use roc_module::symbol::{IdentIds, Interns, ModuleIds}; use roc_types::subs::VarStore; use std::path::Path; @@ -196,9 +200,11 @@ pub mod test_ed_model { let file_path = Path::new(""); let dep_idents = IdentIds::exposed_builtins(8); - let exposed_ident_ids = IdentIds::default(); - let mod_id = ed_model_refs.module_ids.get_or_insert(&"ModId123".into()); + let mod_id = ed_model_refs + .interns + .module_ids + .get_or_insert(&"ModId123".into()); let env = Env::new( mod_id, @@ -206,11 +212,17 @@ pub mod test_ed_model { &mut ed_model_refs.env_pool, &mut ed_model_refs.var_store, dep_idents, - &ed_model_refs.module_ids, + &ed_model_refs.interns.module_ids, exposed_ident_ids, ); - ed_model::init_model(&code_str, file_path, env, &ed_model_refs.code_arena) + ed_model::init_model( + &code_str, + file_path, + env, + &ed_model_refs.interns, + &ed_model_refs.code_arena, + ) } pub struct EdModelRefs { @@ -218,7 +230,7 @@ pub mod test_ed_model { env_arena: Bump, env_pool: Pool, var_store: VarStore, - module_ids: ModuleIds, + interns: Interns, } pub fn init_model_refs() -> EdModelRefs { @@ -227,7 +239,10 @@ pub mod test_ed_model { env_arena: Bump::new(), env_pool: Pool::with_capacity(1024), var_store: VarStore::default(), - module_ids: ModuleIds::default(), + interns: Interns { + module_ids: ModuleIds::default(), + all_ident_ids: IdentIds::exposed_builtins(8), + }, } } diff --git a/editor/src/editor/mvc/ed_update.rs b/editor/src/editor/mvc/ed_update.rs index 5562261a69..859f01cece 100644 --- a/editor/src/editor/mvc/ed_update.rs +++ b/editor/src/editor/mvc/ed_update.rs @@ -13,6 +13,7 @@ use crate::editor::mvc::ed_model::EdModel; use crate::editor::mvc::ed_model::SelectedExpression; use crate::editor::mvc::int_update::start_new_int; use crate::editor::mvc::int_update::update_int; +use crate::editor::mvc::list_update::{prep_empty_list, start_new_list}; use crate::editor::mvc::lookup_update::update_invalid_lookup; use crate::editor::mvc::record_update::start_new_record; use crate::editor::mvc::record_update::update_empty_record; @@ -265,7 +266,7 @@ impl<'a> EdModel<'a> { let content = subs.get(var).content; PoolStr::new( - &content_to_string(content, &subs, self.module.env.home, &Default::default()), + &content_to_string(content, &subs, self.module.env.home, self.interns), self.module.env.pool, ) } @@ -581,11 +582,18 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult '0'..='9' => { start_new_int(ed_model, ch)? } + '[' => { + // this can also be a tag union or become a set, assuming list for now + start_new_list(ed_model)? + } _ => InputOutcome::Ignored } } else if let Some(prev_mark_node_id) = prev_mark_node_id_opt{ if prev_mark_node_id == curr_mark_node_id { match ast_node_ref { + Expr2::SmallInt{ .. } => { + update_int(ed_model, curr_mark_node_id, ch)? + } Expr2::SmallStr(old_arr_str) => { update_small_string( &ch, old_arr_str, ed_model @@ -622,9 +630,6 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult InputOutcome::Ignored } } - Expr2::SmallInt{ .. } => { - update_int(ed_model, curr_mark_node_id, ch)? - } _ => InputOutcome::Ignored } } else if ch.is_ascii_alphanumeric() { // prev_mark_node_id != curr_mark_node_id @@ -643,6 +648,9 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult let prev_node_ref = ed_model.module.env.pool.get(prev_ast_node_id); match prev_node_ref { + Expr2::SmallInt{ .. } => { + update_int(ed_model, prev_mark_node_id, ch)? + } Expr2::InvalidLookup(old_pool_str) => { update_invalid_lookup( &ch.to_string(), @@ -676,8 +684,19 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult InputOutcome::Ignored } } - Expr2::SmallInt{ .. } => { - update_int(ed_model, prev_mark_node_id, ch)? + Expr2::List{ elem_var: _, elems: _} => { + let prev_mark_node = ed_model.markup_node_pool.get(prev_mark_node_id); + + if prev_mark_node.get_content()? == nodes::LEFT_SQUARE_BR { + if curr_mark_node.get_content()? == nodes::RIGHT_SQUARE_BR { + prep_empty_list(ed_model)?; // insert a Blank first, this results in cleaner code + handle_new_char(received_char, ed_model)? + } else { + InputOutcome::Ignored + } + } else { + InputOutcome::Ignored + } } _ => { match ast_node_ref { @@ -707,6 +726,19 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult } else { InputOutcome::Ignored } + } else if "\"{[".contains(*ch) { + let prev_mark_node = ed_model.markup_node_pool.get(prev_mark_node_id); + + if prev_mark_node.get_content()? == nodes::LEFT_SQUARE_BR { + if curr_mark_node.get_content()? == nodes::RIGHT_SQUARE_BR { + prep_empty_list(ed_model)?; // insert a Blank first, this results in cleaner code + handle_new_char(received_char, ed_model)? + } else { + InputOutcome::Ignored + } + } else { + InputOutcome::Ignored + } } else { InputOutcome::Ignored } @@ -1466,6 +1498,90 @@ pub mod test_ed_update { Ok(()) } + #[test] + fn test_list() -> Result<(), String> { + assert_insert(&["┃"], &["[ ┃ ]"], '[')?; + + assert_insert_seq(&["┃"], &["[ 0┃ ]"], "[0")?; + assert_insert_seq(&["┃"], &["[ 1┃ ]"], "[1")?; + assert_insert_seq(&["┃"], &["[ 9┃ ]"], "[9")?; + + assert_insert_seq(&["┃"], &["[ \"┃\" ]"], "[\"")?; + assert_insert_seq( + &["┃"], + &["[ \"hello, hello.0123456789ZXY{}[]-><-┃\" ]"], + "[\"hello, hello.0123456789ZXY{}[]-><-", + )?; + + assert_insert_seq(&["┃"], &["[ { ┃ } ]"], "[{")?; + assert_insert_seq(&["┃"], &["[ { a┃ } ]"], "[{a")?; + assert_insert_seq( + &["┃"], + &["[ { camelCase: { zulu: \"nested┃\" } } ]"], + "[{camelCase:{zulu:\"nested", + )?; + + assert_insert_seq(&["┃"], &["[ [ ┃ ] ]"], "[[")?; + assert_insert_seq(&["┃"], &["[ [ [ ┃ ] ] ]"], "[[[")?; + assert_insert_seq(&["┃"], &["[ [ 0┃ ] ]"], "[[0")?; + assert_insert_seq(&["┃"], &["[ [ \"abc┃\" ] ]"], "[[\"abc")?; + assert_insert_seq( + &["┃"], + &["[ [ { camelCase: { a: 79000┃ } } ] ]"], + "[[{camelCase:{a:79000", + )?; + + Ok(()) + } + + #[test] + fn test_ignore_list() -> Result<(), String> { + assert_insert_seq_ignore(&["┃[ ]"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[ ]┃"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[┃ ]"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[ ┃]"], IGNORE_CHARS)?; + + assert_insert_seq_ignore(&["┃[ 0 ]"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[ 0 ]┃"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[┃ 0 ]"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[ 0 ┃]"], IGNORE_CHARS)?; + + assert_insert_seq_ignore(&["┃[ 137 ]"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[ 137 ]┃"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[┃ 137 ]"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[ 137 ┃]"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[ ┃137 ]"], IGNORE_NO_NUM)?; + assert_insert_seq_ignore(&["[ 137┃ ]"], IGNORE_NO_NUM)?; + + assert_insert_seq_ignore(&["┃[ \"teststring\" ]"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[ \"teststring\" ]┃"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[┃ \"teststring\" ]"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[ \"teststring\" ┃]"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[ ┃\"teststring\" ]"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[ \"teststring\"┃ ]"], IGNORE_CHARS)?; + + assert_insert_seq_ignore(&["┃[ { a: 1 } ]"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[ { a: 1 } ]┃"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[┃ { a: 1 } ]"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[ { a: 1 } ┃]"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[ ┃{ a: 1 } ]"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[ {┃ a: 1 } ]"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[ { a:┃ 1 } ]"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[ { a: 1 ┃} ]"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[ { a: 1 }┃ ]"], IGNORE_CHARS)?; + + assert_insert_seq_ignore(&["┃[ [ ] ]"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[ [ ] ]┃"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[┃ [ ] ]"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[ [ ] ┃]"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[ ┃[ ] ]"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[ [ ]┃ ]"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[ [┃ ] ]"], IGNORE_CHARS)?; + assert_insert_seq_ignore(&["[ [ ┃] ]"], IGNORE_CHARS)?; + + Ok(()) + } + // Create ed_model from pre_lines DSL, do ctrl+shift+up as many times as repeat. // check if modified ed_model has expected string representation of code, caret position and active selection. pub fn assert_ctrl_shift_up_repeat( @@ -1813,6 +1929,30 @@ pub mod test_ed_update { ], )?; + assert_type_tooltip(&["┃"], "List *", '[')?; + assert_type_tooltips_seq(&["┃"], &vec!["List (Num *)"], "[0")?; + assert_type_tooltips_seq(&["┃"], &vec!["List (Num *)", "List (List (Num *))"], "[[0")?; + assert_type_tooltips_seq(&["┃"], &vec!["Str", "List Str"], "[\"a")?; + assert_type_tooltips_seq( + &["┃"], + &vec![ + "Str", + "List Str", + "List (List Str)", + "List (List (List Str))", + ], + "[[[\"a", + )?; + assert_type_tooltips_seq( + &["┃"], + &vec![ + "{ a : Num * }", + "List { a : Num * }", + "List (List { a : Num * })", + ], + "[[{a:1", + )?; + Ok(()) } diff --git a/editor/src/editor/mvc/list_update.rs b/editor/src/editor/mvc/list_update.rs new file mode 100644 index 0000000000..8533967209 --- /dev/null +++ b/editor/src/editor/mvc/list_update.rs @@ -0,0 +1,164 @@ +use crate::editor::ed_error::EdResult; +use crate::editor::ed_error::{MissingParent, UnexpectedASTNode, UnexpectedEmptyPoolVec}; +use crate::editor::markup::attribute::Attributes; +use crate::editor::markup::nodes; +use crate::editor::markup::nodes::MarkupNode; +use crate::editor::mvc::app_update::InputOutcome; +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::syntax_highlight::HighlightStyle; +use crate::lang::ast::expr2_to_string; +use crate::lang::ast::Expr2; +use crate::lang::pool::PoolVec; +use snafu::OptionExt; + +pub fn start_new_list(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 expr2_node = Expr2::List { + elem_var: ed_model.module.env.var_store.fresh(), + elems: PoolVec::empty(ed_model.module.env.pool), + }; + + let mark_node_pool = &mut ed_model.markup_node_pool; + + ed_model.module.env.pool.set(ast_node_id, expr2_node); + + let left_bracket_node = MarkupNode::Text { + content: nodes::LEFT_SQUARE_BR.to_owned(), + ast_node_id, + syn_high_style: HighlightStyle::Bracket, + attributes: Attributes::new(), + parent_id_opt: Some(curr_mark_node_id), // current node will be replaced with nested one + }; + + let left_bracket_node_id = mark_node_pool.add(left_bracket_node); + + let right_bracket_node = MarkupNode::Text { + content: nodes::RIGHT_SQUARE_BR.to_owned(), + ast_node_id, + syn_high_style: HighlightStyle::Bracket, + attributes: Attributes::new(), + parent_id_opt: Some(curr_mark_node_id), // current node will be replaced with nested one + }; + + 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); + + // remove data corresponding to Blank node + ed_model.del_at_line(old_caret_pos.line, old_caret_pos.column)?; + + ed_model.simple_move_carets_right(nodes::LEFT_SQUARE_BR.len()); + + // update GridNodeMap and CodeLines + ed_model.insert_between_line( + old_caret_pos.line, + old_caret_pos.column, + nodes::LEFT_SQUARE_BR, + left_bracket_node_id, + )?; + + ed_model.insert_between_line( + old_caret_pos.line, + old_caret_pos.column + nodes::LEFT_SQUARE_BR.len(), + nodes::RIGHT_SQUARE_BR, + right_bracket_node_id, + )?; + + Ok(InputOutcome::Accepted) + } else { + Ok(InputOutcome::Ignored) + } +} + +// insert Blank at current position for easy code reuse +pub fn prep_empty_list(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 blank_elt = Expr2::Blank; + + let list_ast_node = ed_model.module.env.pool.get(ast_node_id); + + match list_ast_node { + Expr2::List { elem_var, elems: _ } => { + let children: Vec = vec![blank_elt]; + let children_pool_vec = PoolVec::new(children.into_iter(), ed_model.module.env.pool); + + let blank_elt_id = + children_pool_vec + .iter_node_ids() + .next() + .context(UnexpectedEmptyPoolVec { + descriptive_vec_name: "\"children of List AST node\"", + })?; + + let new_list_node = Expr2::List { + elem_var: *elem_var, + elems: children_pool_vec, + }; + + ed_model.module.env.pool.set(ast_node_id, new_list_node); + + let blank_mark_node = MarkupNode::Blank { + ast_node_id: blank_elt_id, + syn_high_style: HighlightStyle::Blank, + attributes: Attributes::new(), + parent_id_opt, + }; + + let blank_mark_node_id = ed_model.markup_node_pool.add(blank_mark_node); + + // add blank mark node to nested mark node from list + if let Some(parent_id) = parent_id_opt { + let parent = ed_model.markup_node_pool.get_mut(parent_id); + + let new_child_index = 1; // 1 because left bracket is first element + + parent.add_child_at_index(new_child_index, blank_mark_node_id)?; + } else { + MissingParent { + node_id: curr_mark_node_id, + } + .fail()? + } + + // update GridNodeMap and CodeLines + ed_model.insert_between_line( + old_caret_pos.line, + old_caret_pos.column, + nodes::BLANK_PLACEHOLDER, + blank_mark_node_id, + )?; + + Ok(InputOutcome::Accepted) + } + _ => UnexpectedASTNode { + required_node_type: "List".to_string(), + encountered_node_type: expr2_to_string(ast_node_id, ed_model.module.env.pool), + } + .fail()?, + } +} diff --git a/editor/src/editor/mvc/mod.rs b/editor/src/editor/mvc/mod.rs index b393cfd0a2..4088a4a1ad 100644 --- a/editor/src/editor/mvc/mod.rs +++ b/editor/src/editor/mvc/mod.rs @@ -4,6 +4,7 @@ pub mod ed_model; pub mod ed_update; pub mod ed_view; mod int_update; +mod list_update; mod lookup_update; mod record_update; mod string_update; diff --git a/editor/src/editor/resources/strings.rs b/editor/src/editor/resources/strings.rs index d4731e8174..366e34f813 100644 --- a/editor/src/editor/resources/strings.rs +++ b/editor/src/editor/resources/strings.rs @@ -1,3 +1,3 @@ pub const NOTHING_OPENED: &str = "Execute `cargo run edit ` to open a file."; pub const START_TIP: &str = - "Start by typing '{', '\"' or a number.\nInput chars that would create parse errors will be ignored."; + "Start by typing '[', '{', '\"' or a number.\nInput chars that would create parse errors will be ignored."; diff --git a/editor/src/lang/ast.rs b/editor/src/lang/ast.rs index b1b75b0554..4b95b1dc62 100644 --- a/editor/src/lang/ast.rs +++ b/editor/src/lang/ast.rs @@ -347,8 +347,9 @@ impl RecordField { pub fn expr2_to_string(node_id: NodeId, pool: &Pool) -> String { let mut full_string = String::new(); + let expr2 = pool.get(node_id); - expr2_to_string_helper(node_id, 0, pool, &mut full_string); + expr2_to_string_helper(expr2, 0, pool, &mut full_string); full_string } @@ -361,13 +362,11 @@ fn get_spacing(indent_level: usize) -> String { } fn expr2_to_string_helper( - node_id: NodeId, + expr2: &Expr2, indent_level: usize, pool: &Pool, out_string: &mut String, ) { - let expr2 = pool.get(node_id); - out_string.push_str(&get_spacing(indent_level)); match expr2 { @@ -384,11 +383,7 @@ fn expr2_to_string_helper( Expr2::EmptyRecord => out_string.push_str("EmptyRecord"), Expr2::Record { record_var, fields } => { out_string.push_str("Record:\n"); - out_string.push_str(&format!( - "{}Var({:?})\n", - get_spacing(indent_level + 1), - record_var - )); + out_string.push_str(&var_to_string(&record_var, indent_level + 1)); out_string.push_str(&format!("{}fields: [\n", get_spacing(indent_level + 1))); @@ -427,7 +422,8 @@ fn expr2_to_string_helper( var, )); - expr2_to_string_helper(*val_node_id, indent_level + 3, pool, out_string); + let val_expr2 = pool.get(*val_node_id); + expr2_to_string_helper(val_expr2, indent_level + 3, pool, out_string); out_string.push_str(&format!("{})\n", get_spacing(indent_level + 2))); } } @@ -435,6 +431,25 @@ fn expr2_to_string_helper( out_string.push_str(&format!("{}]\n", get_spacing(indent_level + 1))); } + Expr2::List { elem_var, elems } => { + out_string.push_str("List:\n"); + out_string.push_str(&var_to_string(elem_var, indent_level + 1)); + out_string.push_str(&format!("{}elems: [\n", get_spacing(indent_level + 1))); + + let mut first_elt = true; + + for elem_expr2 in elems.iter(pool) { + if !first_elt { + out_string.push_str(", ") + } else { + first_elt = false; + } + + expr2_to_string_helper(elem_expr2, indent_level + 2, pool, out_string) + } + + out_string.push_str(&format!("{}]\n", get_spacing(indent_level + 1))); + } Expr2::InvalidLookup(pool_str) => { out_string.push_str(&format!("InvalidLookup({})", pool_str.as_str(pool))); } @@ -447,6 +462,10 @@ fn expr2_to_string_helper( out_string.push('\n'); } +fn var_to_string(some_var: &Variable, indent_level: usize) -> String { + format!("{}Var({:?})\n", get_spacing(indent_level + 1), some_var) +} + #[test] fn size_of_expr() { assert_eq!(std::mem::size_of::(), crate::lang::pool::NODE_BYTES); diff --git a/editor/tests/solve_expr2.rs b/editor/tests/solve_expr2.rs index 374afd6590..9e9d593e5d 100644 --- a/editor/tests/solve_expr2.rs +++ b/editor/tests/solve_expr2.rs @@ -62,7 +62,6 @@ fn infer_eq(actual: &str, expected_str: &str) { let mut var_store = VarStore::default(); let var = var_store.fresh(); let dep_idents = IdentIds::exposed_builtins(8); - let exposed_ident_ids = IdentIds::default(); let mut module_ids = ModuleIds::default(); let mod_id = module_ids.get_or_insert(&"ModId123".into()); @@ -121,6 +120,7 @@ fn infer_eq(actual: &str, expected_str: &str) { module_ids, all_ident_ids: dep_idents, }; + let actual_str = content_to_string(content, &subs, mod_id, &interns); assert_eq!(actual_str, expected_str);