From 0657e21fdb265fbb58f7cd5f634fb1634517aaf6 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Mon, 28 Jun 2021 15:54:19 +0200 Subject: [PATCH 1/5] can make empty list in editor, panic on type tooltip --- compiler/module/src/symbol.rs | 4 ++ compiler/mono/src/alias_analysis.rs | 4 +- compiler/types/src/pretty_print.rs | 2 + editor/src/editor/markup/nodes.rs | 2 + editor/src/editor/mvc/ed_model.rs | 1 - editor/src/editor/mvc/ed_update.rs | 28 ++++++++- editor/src/editor/mvc/list_update.rs | 88 ++++++++++++++++++++++++++++ editor/src/editor/mvc/mod.rs | 1 + editor/src/lang/ast.rs | 43 ++++++++++---- editor/tests/solve_expr2.rs | 4 +- 10 files changed, 160 insertions(+), 17 deletions(-) create mode 100644 editor/src/editor/mvc/list_update.rs diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index d107bdffe4..b482200b32 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -66,6 +66,10 @@ impl Symbol { } pub fn ident_string(self, interns: &Interns) -> &InlinableString { + + dbg!(&self.module_id()); + dbg!(interns); + println!("DONE"); let ident_ids = interns .all_ident_ids .get(&self.module_id()) diff --git a/compiler/mono/src/alias_analysis.rs b/compiler/mono/src/alias_analysis.rs index 5a59917ed8..beb8c79917 100644 --- a/compiler/mono/src/alias_analysis.rs +++ b/compiler/mono/src/alias_analysis.rs @@ -166,9 +166,9 @@ where p.build()? }; - if DEBUG { + /*if DEBUG { eprintln!("{}", program.to_source_string()); - } + }*/ morphic_lib::solve(program) } diff --git a/compiler/types/src/pretty_print.rs b/compiler/types/src/pretty_print.rs index 8df08669d1..98ab8c65b2 100644 --- a/compiler/types/src/pretty_print.rs +++ b/compiler/types/src/pretty_print.rs @@ -356,6 +356,8 @@ fn write_content(env: &Env, content: Content, subs: &Subs, buf: &mut String, par fn write_flat_type(env: &Env, flat_type: FlatType, subs: &Subs, buf: &mut String, parens: Parens) { use crate::subs::FlatType::*; + println!("REMOVE ME"); + match flat_type { Apply(symbol, args) => write_apply(env, symbol, args, subs, buf, parens), EmptyRecord => buf.push_str(EMPTY_RECORD), diff --git a/editor/src/editor/markup/nodes.rs b/editor/src/editor/markup/nodes.rs index 2d09f21ed6..84864225b2 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 = "\"\""; diff --git a/editor/src/editor/mvc/ed_model.rs b/editor/src/editor/mvc/ed_model.rs index 64b36ecf7e..64a264a7a9 100644 --- a/editor/src/editor/mvc/ed_model.rs +++ b/editor/src/editor/mvc/ed_model.rs @@ -196,7 +196,6 @@ 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()); diff --git a/editor/src/editor/mvc/ed_update.rs b/editor/src/editor/mvc/ed_update.rs index 5562261a69..7199035865 100644 --- a/editor/src/editor/mvc/ed_update.rs +++ b/editor/src/editor/mvc/ed_update.rs @@ -21,6 +21,7 @@ use crate::editor::mvc::record_update::update_record_field; use crate::editor::mvc::string_update::start_new_string; use crate::editor::mvc::string_update::update_small_string; use crate::editor::mvc::string_update::update_string; +use crate::editor::mvc::list_update::start_new_list; use crate::editor::slow_pool::MarkNodeId; use crate::editor::slow_pool::SlowPool; use crate::editor::syntax_highlight::HighlightStyle; @@ -44,7 +45,7 @@ use bumpalo::Bump; use roc_can::expected::Expected; use roc_collections::all::MutMap; use roc_module::ident::Lowercase; -use roc_module::symbol::Symbol; +use roc_module::symbol::{Interns, Symbol}; use roc_region::all::Region; use roc_types::solved_types::Solved; use roc_types::subs::{Subs, Variable}; @@ -264,8 +265,13 @@ impl<'a> EdModel<'a> { let content = subs.get(var).content; + let interns = Interns { + module_ids: *self.module.env.module_ids, + all_ident_ids: self.module.env.dep_idents, + }; + PoolStr::new( - &content_to_string(content, &subs, self.module.env.home, &Default::default()), + &content_to_string(content, &subs, self.module.env.home, &interns), self.module.env.pool, ) } @@ -581,6 +587,10 @@ 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{ @@ -1724,6 +1734,20 @@ pub mod test_ed_update { Ok(()) } + #[test] + fn test_ctrl_shift_up_list() -> Result<(), String> { + assert_ctrl_shift_up(&["[ ┃ ]"], &["┃❮[ ]❯"])?; + assert_ctrl_shift_up(&["[┃ ]"], &["┃❮[ ]❯"])?; + assert_ctrl_shift_up(&["[ ┃]"], &["┃❮[ ]❯"])?; + assert_ctrl_shift_up(&["┃[ ]"], &["┃❮[ ]❯"])?; + assert_ctrl_shift_up(&["[ ]┃"], &["┃❮[ ]❯"])?; + + assert_ctrl_shift_up_repeat(&["[ ┃ ]"], &["┃❮[ ]❯"], 4)?; + + //TODO non-empty list + Ok(()) + } + // Create ed_model from pre_lines DSL, do handle_new_char() with new_char_seq, select current Expr2, // check if generated tooltips match expected_tooltips. pub fn assert_type_tooltips_seq( diff --git a/editor/src/editor/mvc/list_update.rs b/editor/src/editor/mvc/list_update.rs new file mode 100644 index 0000000000..e674fe59ed --- /dev/null +++ b/editor/src/editor/mvc/list_update.rs @@ -0,0 +1,88 @@ + +use crate::editor::ed_error::EdResult; +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; +use crate::lang::pool::{PoolVec}; + +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) + } +} \ No newline at end of file diff --git a/editor/src/editor/mvc/mod.rs b/editor/src/editor/mvc/mod.rs index b393cfd0a2..64ee02304f 100644 --- a/editor/src/editor/mvc/mod.rs +++ b/editor/src/editor/mvc/mod.rs @@ -6,4 +6,5 @@ pub mod ed_view; mod int_update; mod lookup_update; mod record_update; +mod list_update; mod string_update; diff --git a/editor/src/lang/ast.rs b/editor/src/lang/ast.rs index b1b75b0554..d61669e39a 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 + 1, 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,14 @@ 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..91a2964fc9 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); @@ -208,7 +208,7 @@ fn constrain_empty_list() { infer_eq( indoc!( r#" - [] + [ ] "# ), "List *", From 3bec3b9d0386b1a05366f4d65a8b3a45cf8a6787 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Tue, 29 Jun 2021 15:25:16 +0200 Subject: [PATCH 2/5] single element lists, nested lists --- compiler/module/src/symbol.rs | 4 - compiler/types/src/pretty_print.rs | 4 +- editor/src/editor/ed_error.rs | 22 +++- editor/src/editor/main.rs | 10 +- editor/src/editor/markup/nodes.rs | 4 +- editor/src/editor/mvc/ed_model.rs | 28 ++++- editor/src/editor/mvc/ed_update.rs | 148 ++++++++++++++++++++----- editor/src/editor/mvc/list_update.rs | 92 +++++++++++++-- editor/src/editor/mvc/mod.rs | 2 +- editor/src/editor/resources/strings.rs | 2 +- editor/src/lang/ast.rs | 12 +- editor/tests/solve_expr2.rs | 2 +- 12 files changed, 266 insertions(+), 64 deletions(-) diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index b482200b32..d107bdffe4 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -66,10 +66,6 @@ impl Symbol { } pub fn ident_string(self, interns: &Interns) -> &InlinableString { - - dbg!(&self.module_id()); - dbg!(interns); - println!("DONE"); let ident_ids = interns .all_ident_ids .get(&self.module_id()) diff --git a/compiler/types/src/pretty_print.rs b/compiler/types/src/pretty_print.rs index 98ab8c65b2..fe58c39848 100644 --- a/compiler/types/src/pretty_print.rs +++ b/compiler/types/src/pretty_print.rs @@ -355,9 +355,7 @@ fn write_content(env: &Env, content: Content, subs: &Subs, buf: &mut String, par fn write_flat_type(env: &Env, flat_type: FlatType, subs: &Subs, buf: &mut String, parens: Parens) { use crate::subs::FlatType::*; - - println!("REMOVE ME"); - + match flat_type { Apply(symbol, args) => write_apply(env, symbol, args, subs, buf, parens), EmptyRecord => buf.push_str(EMPTY_RECORD), 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 84864225b2..8faf0cbf4b 100644 --- a/editor/src/editor/markup/nodes.rs +++ b/editor/src/editor/markup/nodes.rs @@ -211,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, @@ -238,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 64a264a7a9..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; @@ -197,7 +201,10 @@ pub mod test_ed_model { 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, @@ -205,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 { @@ -217,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 { @@ -226,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 7199035865..e0ad11da87 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; @@ -21,7 +22,6 @@ use crate::editor::mvc::record_update::update_record_field; use crate::editor::mvc::string_update::start_new_string; use crate::editor::mvc::string_update::update_small_string; use crate::editor::mvc::string_update::update_string; -use crate::editor::mvc::list_update::start_new_list; use crate::editor::slow_pool::MarkNodeId; use crate::editor::slow_pool::SlowPool; use crate::editor::syntax_highlight::HighlightStyle; @@ -45,7 +45,7 @@ use bumpalo::Bump; use roc_can::expected::Expected; use roc_collections::all::MutMap; use roc_module::ident::Lowercase; -use roc_module::symbol::{Interns, Symbol}; +use roc_module::symbol::Symbol; use roc_region::all::Region; use roc_types::solved_types::Solved; use roc_types::subs::{Subs, Variable}; @@ -265,13 +265,8 @@ impl<'a> EdModel<'a> { let content = subs.get(var).content; - let interns = Interns { - module_ids: *self.module.env.module_ids, - all_ident_ids: self.module.env.dep_idents, - }; - PoolStr::new( - &content_to_string(content, &subs, self.module.env.home, &interns), + &content_to_string(content, &subs, self.module.env.home, self.interns), self.module.env.pool, ) } @@ -596,6 +591,9 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult } 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 @@ -632,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 @@ -653,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(), @@ -686,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 { @@ -717,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 } @@ -1476,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( @@ -1734,20 +1840,6 @@ pub mod test_ed_update { Ok(()) } - #[test] - fn test_ctrl_shift_up_list() -> Result<(), String> { - assert_ctrl_shift_up(&["[ ┃ ]"], &["┃❮[ ]❯"])?; - assert_ctrl_shift_up(&["[┃ ]"], &["┃❮[ ]❯"])?; - assert_ctrl_shift_up(&["[ ┃]"], &["┃❮[ ]❯"])?; - assert_ctrl_shift_up(&["┃[ ]"], &["┃❮[ ]❯"])?; - assert_ctrl_shift_up(&["[ ]┃"], &["┃❮[ ]❯"])?; - - assert_ctrl_shift_up_repeat(&["[ ┃ ]"], &["┃❮[ ]❯"], 4)?; - - //TODO non-empty list - Ok(()) - } - // Create ed_model from pre_lines DSL, do handle_new_char() with new_char_seq, select current Expr2, // check if generated tooltips match expected_tooltips. pub fn assert_type_tooltips_seq( @@ -1837,6 +1929,8 @@ pub mod test_ed_update { ], )?; + assert_type_tooltip(&["┃"], "List *", '[')?; + Ok(()) } diff --git a/editor/src/editor/mvc/list_update.rs b/editor/src/editor/mvc/list_update.rs index e674fe59ed..8533967209 100644 --- a/editor/src/editor/mvc/list_update.rs +++ b/editor/src/editor/mvc/list_update.rs @@ -1,5 +1,5 @@ - 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; @@ -8,8 +8,10 @@ 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 crate::lang::pool::PoolVec; +use snafu::OptionExt; pub fn start_new_list(ed_model: &mut EdModel) -> EdResult { let NodeContext { @@ -22,11 +24,10 @@ pub fn start_new_list(ed_model: &mut EdModel) -> EdResult { 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 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; @@ -85,4 +86,79 @@ pub fn start_new_list(ed_model: &mut EdModel) -> EdResult { } else { Ok(InputOutcome::Ignored) } -} \ No newline at end of file +} + +// 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 64ee02304f..4088a4a1ad 100644 --- a/editor/src/editor/mvc/mod.rs +++ b/editor/src/editor/mvc/mod.rs @@ -4,7 +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 list_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 d61669e39a..4b95b1dc62 100644 --- a/editor/src/lang/ast.rs +++ b/editor/src/lang/ast.rs @@ -431,7 +431,7 @@ fn expr2_to_string_helper( out_string.push_str(&format!("{}]\n", get_spacing(indent_level + 1))); } - Expr2::List { elem_var, elems} => { + 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))); @@ -445,7 +445,7 @@ fn expr2_to_string_helper( first_elt = false; } - expr2_to_string_helper(elem_expr2, indent_level + 1, pool, out_string) + expr2_to_string_helper(elem_expr2, indent_level + 2, pool, out_string) } out_string.push_str(&format!("{}]\n", get_spacing(indent_level + 1))); @@ -462,12 +462,8 @@ 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 - ) +fn var_to_string(some_var: &Variable, indent_level: usize) -> String { + format!("{}Var({:?})\n", get_spacing(indent_level + 1), some_var) } #[test] diff --git a/editor/tests/solve_expr2.rs b/editor/tests/solve_expr2.rs index 91a2964fc9..bfe7d97866 100644 --- a/editor/tests/solve_expr2.rs +++ b/editor/tests/solve_expr2.rs @@ -120,7 +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); From bf69daa3bc7662638cadfbca6599a7f30d42aa72 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Tue, 29 Jun 2021 16:11:20 +0200 Subject: [PATCH 3/5] more type tooltip tests --- compiler/mono/src/alias_analysis.rs | 4 ++-- compiler/types/src/pretty_print.rs | 2 +- editor/src/editor/mvc/ed_update.rs | 7 ++++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/compiler/mono/src/alias_analysis.rs b/compiler/mono/src/alias_analysis.rs index ebd467954d..2b4354d26e 100644 --- a/compiler/mono/src/alias_analysis.rs +++ b/compiler/mono/src/alias_analysis.rs @@ -166,9 +166,9 @@ where p.build()? }; - /*if DEBUG { + if DEBUG { eprintln!("{}", program.to_source_string()); - }*/ + } morphic_lib::solve(program) } diff --git a/compiler/types/src/pretty_print.rs b/compiler/types/src/pretty_print.rs index fe58c39848..8df08669d1 100644 --- a/compiler/types/src/pretty_print.rs +++ b/compiler/types/src/pretty_print.rs @@ -355,7 +355,7 @@ fn write_content(env: &Env, content: Content, subs: &Subs, buf: &mut String, par fn write_flat_type(env: &Env, flat_type: FlatType, subs: &Subs, buf: &mut String, parens: Parens) { use crate::subs::FlatType::*; - + match flat_type { Apply(symbol, args) => write_apply(env, symbol, args, subs, buf, parens), EmptyRecord => buf.push_str(EMPTY_RECORD), diff --git a/editor/src/editor/mvc/ed_update.rs b/editor/src/editor/mvc/ed_update.rs index e0ad11da87..9e19802dd8 100644 --- a/editor/src/editor/mvc/ed_update.rs +++ b/editor/src/editor/mvc/ed_update.rs @@ -1930,7 +1930,12 @@ 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(()) } From df4008438e5c9fe314f3a2b16af30356b53d1974 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Tue, 29 Jun 2021 16:14:35 +0200 Subject: [PATCH 4/5] fmt --- editor/src/editor/mvc/ed_update.rs | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/editor/src/editor/mvc/ed_update.rs b/editor/src/editor/mvc/ed_update.rs index 9e19802dd8..859f01cece 100644 --- a/editor/src/editor/mvc/ed_update.rs +++ b/editor/src/editor/mvc/ed_update.rs @@ -1933,9 +1933,26 @@ pub mod test_ed_update { 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")?; - + 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(()) } From 166c8ac2176ef0901600e45705c7f89f92c24008 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Tue, 29 Jun 2021 16:57:00 +0200 Subject: [PATCH 5/5] undo list space change --- editor/tests/solve_expr2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/tests/solve_expr2.rs b/editor/tests/solve_expr2.rs index bfe7d97866..9e9d593e5d 100644 --- a/editor/tests/solve_expr2.rs +++ b/editor/tests/solve_expr2.rs @@ -208,7 +208,7 @@ fn constrain_empty_list() { infer_eq( indoc!( r#" - [ ] + [] "# ), "List *",