From 80915105bca0c6689df7f01a3992c871fc2b676a Mon Sep 17 00:00:00 2001 From: rvcas Date: Fri, 19 Mar 2021 14:20:05 -0400 Subject: [PATCH 01/32] feat(unify): start unifying a single-tag union no payload with Func --- compiler/solve/tests/solve_expr.rs | 42 ++++++++++++++++++++++++++++++ compiler/unify/src/unify.rs | 22 ++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index df49d7fc68..69337b5e69 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -566,6 +566,48 @@ mod solve_expr { ); } + #[test] + fn applied_tag_function() { + infer_eq_without_problem( + indoc!( + r#" + foo : [ Foo Str ]* + + List.map [ "a", "b" ] Foo + "# + ), + "List [ Foo Str ]*", + ) + } + + #[test] + fn applied_tag_function_other() { + infer_eq_without_problem( + indoc!( + r#" + foo : [ Foo Str ]* + + Foo "hi" + "# + ), + "[ Foo Str ]*", + ) + } + + #[test] + fn applied_tag() { + infer_eq_without_problem( + indoc!( + r#" + foo : [ Foo Str ]* + + List.map [ "a", "b" ] \elem -> Foo elem + "# + ), + "List [ Foo Str ]*", + ) + } + #[test] fn def_2_arg_closure() { infer_eq( diff --git a/compiler/unify/src/unify.rs b/compiler/unify/src/unify.rs index b0ff2baea5..df8dafecbe 100644 --- a/compiler/unify/src/unify.rs +++ b/compiler/unify/src/unify.rs @@ -1069,6 +1069,28 @@ fn unify_flat_type( problems } } + (TagUnion(tags, ext), Func(args, closure, ret)) if tags.len() == 1 => { + let (tag, payload) = tags.iter().next().unwrap(); + + if payload.len() == 0 { + let mut new_payload = vec![]; + let mut new_tags = MutMap::default(); + + for arg in args { + new_payload.push(*arg); + } + + new_tags.insert(tag.clone(), new_payload); + + vec![] + } else { + mismatch!( + "Trying to unify two flat types that are incompatible: {:?} ~ {:?}", + TagUnion(tags.clone(), ext.clone()), + Func(args.clone(), closure.clone(), ret.clone()) + ) + } + } (other1, other2) => mismatch!( "Trying to unify two flat types that are incompatible: {:?} ~ {:?}", other1, From 7f7386c6398e7292960d1dcb9abb11506132baee Mon Sep 17 00:00:00 2001 From: rvcas Date: Sun, 21 Mar 2021 00:17:20 -0400 Subject: [PATCH 02/32] feat(unify): basic tag functions seem to work --- compiler/unify/src/unify.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/compiler/unify/src/unify.rs b/compiler/unify/src/unify.rs index df8dafecbe..53c05fc4ef 100644 --- a/compiler/unify/src/unify.rs +++ b/compiler/unify/src/unify.rs @@ -1072,7 +1072,7 @@ fn unify_flat_type( (TagUnion(tags, ext), Func(args, closure, ret)) if tags.len() == 1 => { let (tag, payload) = tags.iter().next().unwrap(); - if payload.len() == 0 { + if payload.is_empty() { let mut new_payload = vec![]; let mut new_tags = MutMap::default(); @@ -1082,7 +1082,20 @@ fn unify_flat_type( new_tags.insert(tag.clone(), new_payload); - vec![] + let content = Structure(TagUnion(new_tags, *ext)); + + let new_tag_union_var = fresh(subs, pool, ctx, content); + + let problems = unify_pool(subs, pool, new_tag_union_var, *ret); + + if problems.is_empty() { + let desc = subs.get(ctx.second); + subs.union(ctx.first, ctx.second, desc); + + vec![] + } else { + problems + } } else { mismatch!( "Trying to unify two flat types that are incompatible: {:?} ~ {:?}", @@ -1091,6 +1104,9 @@ fn unify_flat_type( ) } } + (Func(_args, _closure, _ret), TagUnion(tags, _ext)) if tags.len() == 1 => { + todo!("other way") + } (other1, other2) => mismatch!( "Trying to unify two flat types that are incompatible: {:?} ~ {:?}", other1, From 3098bcf61d51d2533bec2e0e73eec84cb4fc4467 Mon Sep 17 00:00:00 2001 From: rvcas Date: Sun, 21 Mar 2021 00:28:52 -0400 Subject: [PATCH 03/32] feat(unify): Func, TagUnion add the other way --- compiler/unify/src/unify.rs | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/compiler/unify/src/unify.rs b/compiler/unify/src/unify.rs index 53c05fc4ef..d52f8f04ef 100644 --- a/compiler/unify/src/unify.rs +++ b/compiler/unify/src/unify.rs @@ -1104,8 +1104,40 @@ fn unify_flat_type( ) } } - (Func(_args, _closure, _ret), TagUnion(tags, _ext)) if tags.len() == 1 => { - todo!("other way") + (Func(args, closure, ret), TagUnion(tags, ext)) if tags.len() == 1 => { + let (tag, payload) = tags.iter().next().unwrap(); + + if payload.is_empty() { + let mut new_payload = vec![]; + let mut new_tags = MutMap::default(); + + for arg in args { + new_payload.push(*arg); + } + + new_tags.insert(tag.clone(), new_payload); + + let content = Structure(TagUnion(new_tags, *ext)); + + let new_tag_union_var = fresh(subs, pool, ctx, content); + + let problems = unify_pool(subs, pool, *ret, new_tag_union_var); + + if problems.is_empty() { + let desc = subs.get(ctx.second); + subs.union(ctx.first, ctx.second, desc); + + vec![] + } else { + problems + } + } else { + mismatch!( + "Trying to unify two flat types that are incompatible: {:?} ~ {:?}", + Func(args.clone(), closure.clone(), ret.clone()), + TagUnion(tags.clone(), ext.clone()), + ) + } } (other1, other2) => mismatch!( "Trying to unify two flat types that are incompatible: {:?} ~ {:?}", From 36ec1a0f56a305f87c24ad736d08a40f3e276d2e Mon Sep 17 00:00:00 2001 From: rvcas Date: Sun, 21 Mar 2021 00:38:00 -0400 Subject: [PATCH 04/32] feat(unify): clippy warnings --- compiler/unify/src/unify.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/unify/src/unify.rs b/compiler/unify/src/unify.rs index d52f8f04ef..ed9789f2f7 100644 --- a/compiler/unify/src/unify.rs +++ b/compiler/unify/src/unify.rs @@ -1099,8 +1099,8 @@ fn unify_flat_type( } else { mismatch!( "Trying to unify two flat types that are incompatible: {:?} ~ {:?}", - TagUnion(tags.clone(), ext.clone()), - Func(args.clone(), closure.clone(), ret.clone()) + TagUnion(tags.clone(), *ext), + Func(args.clone(), *closure, *ret) ) } } @@ -1134,8 +1134,8 @@ fn unify_flat_type( } else { mismatch!( "Trying to unify two flat types that are incompatible: {:?} ~ {:?}", - Func(args.clone(), closure.clone(), ret.clone()), - TagUnion(tags.clone(), ext.clone()), + Func(args.clone(), *closure, *ret), + TagUnion(tags.clone(), *ext), ) } } From eff822f48f040094face87cc33fd32125cc03e66 Mon Sep 17 00:00:00 2001 From: rvcas Date: Sun, 21 Mar 2021 12:22:42 -0400 Subject: [PATCH 05/32] feat(unify): more tests and some new ones fail --- compiler/solve/tests/solve_expr.rs | 60 ++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index 69337b5e69..713c611530 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -567,13 +567,11 @@ mod solve_expr { } #[test] - fn applied_tag_function() { + fn applied_tag() { infer_eq_without_problem( indoc!( r#" - foo : [ Foo Str ]* - - List.map [ "a", "b" ] Foo + List.map [ "a", "b" ] \elem -> Foo elem "# ), "List [ Foo Str ]*", @@ -581,13 +579,13 @@ mod solve_expr { } #[test] - fn applied_tag_function_other() { + fn applied_tag_function() { infer_eq_without_problem( indoc!( r#" - foo : [ Foo Str ]* + foo = Foo - Foo "hi" + foo "hi" "# ), "[ Foo Str ]*", @@ -595,19 +593,59 @@ mod solve_expr { } #[test] - fn applied_tag() { + fn applied_tag_function_list_map() { infer_eq_without_problem( indoc!( r#" - foo : [ Foo Str ]* - - List.map [ "a", "b" ] \elem -> Foo elem + List.map [ "a", "b" ] Foo "# ), "List [ Foo Str ]*", ) } + #[test] + fn applied_tag_function_list() { + infer_eq_without_problem( + indoc!( + r#" + [ Foo, \x -> Bar x ] + "# + ), + "List (a -> [ Bar a, Foo a ]*)", + ) + } + + #[test] + fn applied_tag_function_record() { + infer_eq_without_problem( + indoc!( + r#" + foo = Foo + + { + x: [ foo, Foo ], + y: [ foo, \x -> Foo x ], + z: [ foo, \x,y -> Foo x y ] + } + "# + ), + "{ x: List (a, b -> [ Foo a b ]*), y: List (a, b -> [ Foo a b ]*), z: List (a, b -> [ Foo a b ]*) }", + ) + } + + #[test] + fn applied_tag_function_mismatch() { + infer_eq_without_problem( + indoc!( + r#" + \foo -> { x: [ foo, Foo ], y: [ foo, \x -> Foo x ] } + "# + ), + "", + ) + } + #[test] fn def_2_arg_closure() { infer_eq( From b063b454811bc2994638088b7c86f79d5483c8dc Mon Sep 17 00:00:00 2001 From: rvcas Date: Mon, 22 Mar 2021 00:50:03 -0400 Subject: [PATCH 06/32] feat(unify): we were actually hitting the second case but desc now comes from first --- compiler/solve/tests/solve_expr.rs | 2 +- compiler/unify/src/unify.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index 713c611530..201836b195 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -630,7 +630,7 @@ mod solve_expr { } "# ), - "{ x: List (a, b -> [ Foo a b ]*), y: List (a, b -> [ Foo a b ]*), z: List (a, b -> [ Foo a b ]*) }", + "{ x : List [ Foo ]*, y : List (a -> [ Foo a ]*), z : List (b, c -> [ Foo b c ]*) }", ) } diff --git a/compiler/unify/src/unify.rs b/compiler/unify/src/unify.rs index ed9789f2f7..464ad0fe47 100644 --- a/compiler/unify/src/unify.rs +++ b/compiler/unify/src/unify.rs @@ -1124,7 +1124,7 @@ fn unify_flat_type( let problems = unify_pool(subs, pool, *ret, new_tag_union_var); if problems.is_empty() { - let desc = subs.get(ctx.second); + let desc = subs.get(ctx.first); subs.union(ctx.first, ctx.second, desc); vec![] From b806df1b92f0edf8c1b46d6c572cdd1751bff896 Mon Sep 17 00:00:00 2001 From: rvcas Date: Mon, 22 Mar 2021 00:55:39 -0400 Subject: [PATCH 07/32] tests(solve_expr): add some comments so know which branch --- compiler/solve/tests/solve_expr.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index 201836b195..fd6e9d5800 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -578,6 +578,7 @@ mod solve_expr { ) } + // Tests (TagUnion, Func) #[test] fn applied_tag_function() { infer_eq_without_problem( @@ -592,6 +593,7 @@ mod solve_expr { ) } + // Tests (TagUnion, Func) #[test] fn applied_tag_function_list_map() { infer_eq_without_problem( @@ -604,8 +606,22 @@ mod solve_expr { ) } + // Tests (TagUnion, Func) #[test] fn applied_tag_function_list() { + infer_eq_without_problem( + indoc!( + r#" + [ \x -> Bar x, Foo ] + "# + ), + "List (a -> [ Bar a, Foo a ]*)", + ) + } + + // Tests (Func, TagUnion) + #[test] + fn applied_tag_function_list_other_way() { infer_eq_without_problem( indoc!( r#" @@ -616,6 +632,7 @@ mod solve_expr { ) } + // Tests (Func, TagUnion) #[test] fn applied_tag_function_record() { infer_eq_without_problem( From eb4a6109d62317a188ff57a202e67fe162890b13 Mon Sep 17 00:00:00 2001 From: rvcas Date: Mon, 22 Mar 2021 11:25:51 -0400 Subject: [PATCH 08/32] tests(solve_expr): try a mismatch --- compiler/solve/tests/solve_expr.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index fd6e9d5800..d777cabb43 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -653,13 +653,16 @@ mod solve_expr { #[test] fn applied_tag_function_mismatch() { - infer_eq_without_problem( + infer_eq( indoc!( r#" - \foo -> { x: [ foo, Foo ], y: [ foo, \x -> Foo x ] } + x : List [ Foo Str ] + x = List.map [ 1, 2 ] Foo + + x "# ), - "", + "", ) } From 3feedb92487c78f804d207faf30b599242c41462 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Tue, 23 Mar 2021 19:38:42 +0100 Subject: [PATCH 09/32] do all tree modifications when user presses { --- editor/editor-ideas.md | 2 +- editor/src/editor/ed_error.rs | 10 +- editor/src/editor/main.rs | 11 +- editor/src/editor/markup/attribute.rs | 16 +- editor/src/editor/markup/nodes.rs | 69 +++--- editor/src/editor/mvc/app_update.rs | 5 +- editor/src/editor/mvc/ed_model.rs | 190 +-------------- editor/src/editor/mvc/ed_update.rs | 330 ++++++++++++++++++++++++++ editor/src/editor/mvc/ed_view.rs | 6 +- editor/src/editor/mvc/mod.rs | 1 + editor/src/editor/slow_pool.rs | 12 +- 11 files changed, 409 insertions(+), 243 deletions(-) create mode 100644 editor/src/editor/mvc/ed_update.rs diff --git a/editor/editor-ideas.md b/editor/editor-ideas.md index 55cb754811..3062572dd8 100644 --- a/editor/editor-ideas.md +++ b/editor/editor-ideas.md @@ -78,7 +78,7 @@ e.g. you have a test `calculate_sum_test` that only uses the function `add`, whe * Searchbar for examples/docs. With permission search strings could be shared with the platform/package authors so they know exactly what their users are struggling with. * Show productivity/feature tips on startup. Show link to page with all tips. Allow not seeing tips next time. * Search friendly editor docs inside the editor. Offer to send search string to Roc maintainers when no results, or if no results were clicked. -* File history timeline view. Show timeline with commits that changed this file, the number of lines added and deleted as well as which user made the changes. +* File history timeline view. Show timeline with commits that changed this file, the number of lines added and deleted as well as which user made the changes. Arrow navigation should allow you to quickly view different versions of the file. * Suggested quick fixes should be directly visible and clickable. Not like in vs code where you put the caret on an error until a lightbulb appears in the margin which you have to click for the fixes to apppear, after which you click to apply the fix you want :( . #### Autocomplete diff --git a/editor/src/editor/ed_error.rs b/editor/src/editor/ed_error.rs index bfef644ae3..5d4aed53df 100644 --- a/editor/src/editor/ed_error.rs +++ b/editor/src/editor/ed_error.rs @@ -1,4 +1,4 @@ -use crate::editor::slow_pool::SlowNodeId; +use crate::editor::slow_pool::MarkNodeId; use colored::*; use snafu::{Backtrace, ErrorCompat, NoneError, ResultExt, Snafu}; @@ -15,7 +15,7 @@ pub enum EdError { node_id ))] CaretNotFound { - node_id: SlowNodeId, + node_id: MarkNodeId, backtrace: Backtrace, }, @@ -42,14 +42,14 @@ pub enum EdError { #[snafu(display("NestedNodeWithoutChildren: tried to retrieve child from Nested MarkupNode with id {} but it had no children.", node_id))] NestedNodeWithoutChildren { - node_id: SlowNodeId, + 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: SlowNodeId, - children_ids: Vec, + node_id: MarkNodeId, + children_ids: Vec, backtrace: Backtrace, }, diff --git a/editor/src/editor/main.rs b/editor/src/editor/main.rs index 4d9ba84c02..7d2218eb6e 100644 --- a/editor/src/editor/main.rs +++ b/editor/src/editor/main.rs @@ -2,7 +2,6 @@ use super::keyboard_input; use super::style::CODE_TXT_XY; use crate::editor::ed_error::print_ui_err; use crate::editor::resources::strings::NOTHING_OPENED; -use crate::editor::slow_pool::SlowPool; use crate::editor::{ config::Config, ed_error::print_err, @@ -132,7 +131,6 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box> { let mut env_pool = Pool::with_capacity(1024); let env_arena = Bump::new(); let code_arena = Bump::new(); - let mut markup_node_pool = SlowPool::new(); let mut var_store = VarStore::default(); let dep_idents = IdentIds::exposed_builtins(8); @@ -173,13 +171,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, - &mut markup_node_pool, - ); + let ed_model_res = ed_model::init_model(&code_str, file_path, env, &code_arena); match ed_model_res { Ok(mut ed_model) => { @@ -304,7 +296,6 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box> { &size, CODE_TXT_XY.into(), &config, - &markup_node_pool, ); match text_and_rects_res { diff --git a/editor/src/editor/markup/attribute.rs b/editor/src/editor/markup/attribute.rs index 304c4aadce..a84525c1a3 100644 --- a/editor/src/editor/markup/attribute.rs +++ b/editor/src/editor/markup/attribute.rs @@ -15,36 +15,36 @@ impl Caret { } } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct SelectionStart { offset_col: usize, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct SelectionEnd { offset_col: usize, } // Highlight is used for example when searching for a specific string to highlight all search results in the module -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct HighlightStart { offset_col: usize, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct HighlightEnd { offset_col: usize, } // Underline is used for warnings and errors -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct UnderlineStart { offset_col: usize, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct UnderlineEnd { offset_col: usize, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Attribute { // Rust does not yet support types for enum variants so we have to do it like this Caret { caret: Caret }, @@ -59,7 +59,7 @@ pub enum Attribute { UnderlineEnd { underline_end: UnderlineEnd }, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Attributes { pub all: Vec, } diff --git a/editor/src/editor/markup/nodes.rs b/editor/src/editor/markup/nodes.rs index 9b1d9be4a9..b25a1c3c51 100644 --- a/editor/src/editor/markup/nodes.rs +++ b/editor/src/editor/markup/nodes.rs @@ -1,5 +1,5 @@ use super::attribute::Attributes; -use crate::editor::slow_pool::SlowNodeId; +use crate::editor::slow_pool::MarkNodeId; use crate::editor::slow_pool::SlowPool; use crate::editor::syntax_highlight::HighlightStyle; use crate::lang::{ @@ -9,40 +9,64 @@ use crate::lang::{ }; use bumpalo::Bump; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum MarkupNode { Nested { ast_node_id: NodeId, - children_ids: Vec, - parent_id_opt: Option, + children_ids: Vec, + parent_id_opt: Option, }, Text { content: String, ast_node_id: NodeId, syn_high_style: HighlightStyle, attributes: Attributes, - parent_id_opt: Option, + parent_id_opt: Option, }, Blank { ast_node_id: NodeId, attributes: Attributes, syn_high_style: HighlightStyle, - parent_id_opt: Option, + parent_id_opt: Option, }, } -pub const BLANK_PLACEHOLDER: &str = " "; +impl MarkupNode { + pub fn get_ast_node_id(&self) -> NodeId { + match self { + MarkupNode::Nested { ast_node_id, .. } => *ast_node_id, + MarkupNode::Text { ast_node_id, .. } => *ast_node_id, + MarkupNode::Blank { ast_node_id, .. } => *ast_node_id, + } + } + + pub fn get_parent_id_opt(&self) -> Option { + match self { + MarkupNode::Nested { parent_id_opt, .. } => *parent_id_opt, + MarkupNode::Text { parent_id_opt, .. } => *parent_id_opt, + MarkupNode::Blank { parent_id_opt, .. } => *parent_id_opt, + } + } + + pub fn is_blank(&self) -> bool { + matches!(self, MarkupNode::Blank { .. }) + } +} fn get_string<'a>(env: &Env<'a>, pool_str: &PoolStr) -> String { pool_str.as_str(env.pool).to_owned() } +pub const BLANK_PLACEHOLDER: &str = " "; +pub const LEFT_ACCOLADE: &str = "{ "; +pub const RIGHT_ACCOLADE: &str = " }"; + fn new_markup_node( text: String, node_id: NodeId, highlight_style: HighlightStyle, markup_node_pool: &mut SlowPool, -) -> SlowNodeId { +) -> MarkNodeId { let node = MarkupNode::Text { content: text, ast_node_id: node_id, @@ -60,7 +84,7 @@ pub fn expr2_to_markup<'a, 'b>( env: &mut Env<'b>, expr2: &Expr2, markup_node_pool: &mut SlowPool, -) -> SlowNodeId { +) -> MarkNodeId { // TODO find way to add current expr2 to pool let node_id = env.pool.add(Expr2::Blank); @@ -138,7 +162,7 @@ pub fn expr2_to_markup<'a, 'b>( let mut children_ids = Vec::new(); children_ids.push(new_markup_node( - "{ ".to_string(), + LEFT_ACCOLADE.to_string(), node_id, HighlightStyle::Bracket, markup_node_pool, @@ -178,7 +202,7 @@ pub fn expr2_to_markup<'a, 'b>( } children_ids.push(new_markup_node( - " }".to_string(), + RIGHT_ACCOLADE.to_string(), node_id, HighlightStyle::Bracket, markup_node_pool, @@ -202,7 +226,7 @@ pub fn expr2_to_markup<'a, 'b>( } } -pub fn set_parent_for_all(markup_node_id: SlowNodeId, markup_node_pool: &mut SlowPool) { +pub fn set_parent_for_all(markup_node_id: MarkNodeId, markup_node_pool: &mut SlowPool) { let node = markup_node_pool.get(markup_node_id); if let MarkupNode::Nested { @@ -221,17 +245,17 @@ pub fn set_parent_for_all(markup_node_id: SlowNodeId, markup_node_pool: &mut Slo } pub fn set_parent_for_all_helper( - markup_node_id: SlowNodeId, - parent_node_id: SlowNodeId, + markup_node_id: MarkNodeId, + parent_node_id: MarkNodeId, markup_node_pool: &mut SlowPool, ) { let node = markup_node_pool.get_mut(markup_node_id); match node { MarkupNode::Nested { - ast_node_id: _, children_ids, parent_id_opt, + .. } => { *parent_id_opt = Some(parent_node_id); @@ -242,18 +266,7 @@ pub fn set_parent_for_all_helper( set_parent_for_all_helper(child_id, markup_node_id, markup_node_pool); } } - MarkupNode::Text { - content: _, - ast_node_id: _, - syn_high_style: _, - attributes: _, - parent_id_opt, - } => *parent_id_opt = Some(parent_node_id), - MarkupNode::Blank { - ast_node_id: _, - attributes: _, - syn_high_style: _, - parent_id_opt, - } => *parent_id_opt = Some(parent_node_id), + MarkupNode::Text { parent_id_opt, .. } => *parent_id_opt = Some(parent_node_id), + MarkupNode::Blank { parent_id_opt, .. } => *parent_id_opt = Some(parent_node_id), } } diff --git a/editor/src/editor/mvc/app_update.rs b/editor/src/editor/mvc/app_update.rs index a6d41572c9..68d89359dc 100644 --- a/editor/src/editor/mvc/app_update.rs +++ b/editor/src/editor/mvc/app_update.rs @@ -1,4 +1,5 @@ use super::app_model::AppModel; +use super::ed_update; use crate::editor::ed_error::EdResult; use crate::ui::text::lines::SelectableLines; use crate::window::keyboard_input::from_winit; @@ -50,10 +51,10 @@ pub fn pass_keydown_to_focused( Ok(()) } -pub fn handle_new_char(_received_char: &char, app_model: &mut AppModel) -> EdResult<()> { +pub fn handle_new_char(received_char: &char, app_model: &mut AppModel) -> EdResult<()> { if let Some(ref mut ed_model) = app_model.ed_model_opt { if ed_model.has_focus { - unimplemented!("TODO"); + ed_update::handle_new_char(received_char, ed_model)?; } } diff --git a/editor/src/editor/mvc/ed_model.rs b/editor/src/editor/mvc/ed_model.rs index 7db649567a..e7898a3ff9 100644 --- a/editor/src/editor/mvc/ed_model.rs +++ b/editor/src/editor/mvc/ed_model.rs @@ -1,5 +1,5 @@ use crate::editor::code_lines::CodeLines; -use crate::editor::slow_pool::{SlowNodeId, SlowPool}; +use crate::editor::slow_pool::{MarkNodeId, SlowPool}; use crate::editor::syntax_highlight::HighlightStyle; use crate::editor::{ ed_error::EdError::ParseError, @@ -12,32 +12,23 @@ use crate::lang::ast::Expr2; use crate::lang::expr::{str_to_expr2, Env}; use crate::lang::scope::Scope; use crate::ui::text::caret_w_select::CaretWSelect; -use crate::ui::text::lines::MoveCaretFun; -use crate::ui::text::selection::validate_raw_sel; -use crate::ui::text::selection::RawSelection; -use crate::ui::text::selection::Selection; -use crate::ui::text::text_pos::TextPos; -use crate::ui::text::{lines, lines::Lines, lines::SelectableLines}; -use crate::ui::ui_error::UIResult; -use crate::window::keyboard_input::Modifiers; use bumpalo::collections::String as BumpString; use bumpalo::Bump; use nonempty::NonEmpty; use roc_region::all::Region; use std::path::Path; -use winit::event::VirtualKeyCode; -use VirtualKeyCode::*; #[derive(Debug)] pub struct EdModel<'a> { pub module: EdModule<'a>, pub file_path: &'a Path, pub code_lines: CodeLines, - pub markup_root_id: SlowNodeId, + pub markup_root_id: MarkNodeId, + pub markup_node_pool: SlowPool, pub glyph_dim_rect_opt: Option, pub has_focus: bool, - // Option: MarkupNode that corresponds to caret position, Option because this SlowNodeId is only calculated when it needs to be used. - pub caret_w_select_vec: NonEmpty<(CaretWSelect, Option)>, + // Option: MarkupNode that corresponds to caret position, Option because this MarkNodeId is only calculated when it needs to be used. + pub caret_w_select_vec: NonEmpty<(CaretWSelect, Option)>, } pub fn init_model<'a>( @@ -45,11 +36,11 @@ pub fn init_model<'a>( file_path: &'a Path, env: Env<'a>, code_arena: &'a Bump, - markup_node_pool: &mut SlowPool, ) -> EdResult> { let mut module = EdModule::new(&code_str, env, code_arena)?; // TODO fix moving issue and insert module.ast_root into pool let ast_root_id = module.env.pool.add(Expr2::Blank); + let mut markup_node_pool = SlowPool::new(); let markup_root_id = if code_str.is_empty() { let blank_root = MarkupNode::Blank { @@ -67,9 +58,9 @@ pub fn init_model<'a>( code_arena, &mut module.env, &module.ast_root, - markup_node_pool, + &mut markup_node_pool, ); - set_parent_for_all(temp_markup_root_id, markup_node_pool); + set_parent_for_all(temp_markup_root_id, &mut markup_node_pool); temp_markup_root_id }; @@ -79,176 +70,13 @@ pub fn init_model<'a>( file_path, code_lines: CodeLines::from_str(code_str), markup_root_id, + markup_node_pool, glyph_dim_rect_opt: None, has_focus: true, caret_w_select_vec: NonEmpty::new((CaretWSelect::default(), None)), }) } -impl<'a> EdModel<'a> { - pub fn move_caret( - &mut self, - move_fun: MoveCaretFun, - modifiers: &Modifiers, - ) -> UIResult<()> { - for caret_tup in self.caret_w_select_vec.iter_mut() { - caret_tup.0 = move_fun(&self.code_lines, caret_tup.0, modifiers)?; - caret_tup.1 = None; - } - - Ok(()) - } -} - -impl<'a> SelectableLines for EdModel<'a> { - fn get_caret(self) -> TextPos { - self.caret_w_select_vec.first().0.caret_pos - } - - // keeps active selection - fn set_caret(&mut self, caret_pos: TextPos) { - let caret_tup = self.caret_w_select_vec.first_mut(); - caret_tup.0.caret_pos = caret_pos; - caret_tup.1 = None; - } - - fn move_caret_left(&mut self, modifiers: &Modifiers) -> UIResult<()> { - let move_fun: MoveCaretFun = lines::move_caret_left; - EdModel::move_caret(self, move_fun, modifiers)?; - - Ok(()) - } - - fn move_caret_right(&mut self, modifiers: &Modifiers) -> UIResult<()> { - let move_fun: MoveCaretFun = lines::move_caret_right; - EdModel::move_caret(self, move_fun, modifiers)?; - - Ok(()) - } - - fn move_caret_up(&mut self, modifiers: &Modifiers) -> UIResult<()> { - let move_fun: MoveCaretFun = lines::move_caret_up; - EdModel::move_caret(self, move_fun, modifiers)?; - - Ok(()) - } - - fn move_caret_down(&mut self, modifiers: &Modifiers) -> UIResult<()> { - let move_fun: MoveCaretFun = lines::move_caret_down; - EdModel::move_caret(self, move_fun, modifiers)?; - - Ok(()) - } - - fn move_caret_home(&mut self, modifiers: &Modifiers) -> UIResult<()> { - let move_fun: MoveCaretFun = lines::move_caret_home; - EdModel::move_caret(self, move_fun, modifiers)?; - - Ok(()) - } - - fn move_caret_end(&mut self, modifiers: &Modifiers) -> UIResult<()> { - let move_fun: MoveCaretFun = lines::move_caret_end; - EdModel::move_caret(self, move_fun, modifiers)?; - - Ok(()) - } - - fn get_selection(&self) -> Option { - self.caret_w_select_vec.first().0.selection_opt - } - - fn is_selection_active(&self) -> bool { - self.get_selection().is_some() - } - - fn get_selected_str(&self) -> UIResult> { - if let Some(selection) = self.get_selection() { - let start_line_index = selection.start_pos.line; - let start_col = selection.start_pos.column; - let end_line_index = selection.end_pos.line; - let end_col = selection.end_pos.column; - - if start_line_index == end_line_index { - let line_ref = self.code_lines.get_line(start_line_index)?; - - Ok(Some(line_ref[start_col..end_col].to_string())) - } else { - let full_str = String::new(); - - // TODO - Ok(Some(full_str)) - } - } else { - Ok(None) - } - } - - fn set_raw_sel(&mut self, raw_sel: RawSelection) -> UIResult<()> { - self.caret_w_select_vec.first_mut().0.selection_opt = Some(validate_raw_sel(raw_sel)?); - - Ok(()) - } - - fn set_sel_none(&mut self) { - self.caret_w_select_vec.first_mut().0.selection_opt = None; - } - - fn set_caret_w_sel(&mut self, caret_w_sel: CaretWSelect) { - self.caret_w_select_vec.first_mut().0 = caret_w_sel; - } - - fn select_all(&mut self) -> UIResult<()> { - if self.code_lines.nr_of_chars() > 0 { - let last_pos = self.last_text_pos()?; - - self.set_raw_sel(RawSelection { - start_pos: TextPos { line: 0, column: 0 }, - end_pos: last_pos, - })?; - - self.set_caret(last_pos); - } - - Ok(()) - } - - fn last_text_pos(&self) -> UIResult { - let nr_of_lines = self.code_lines.lines.len(); - let last_line_index = nr_of_lines - 1; - let last_line = self.code_lines.get_line(last_line_index)?; - - Ok(TextPos { - line: self.code_lines.lines.len() - 1, - column: last_line.len(), - }) - } - - fn handle_key_down( - &mut self, - modifiers: &Modifiers, - virtual_keycode: VirtualKeyCode, - ) -> UIResult<()> { - match virtual_keycode { - Left => self.move_caret_left(modifiers), - Up => self.move_caret_up(modifiers), - Right => self.move_caret_right(modifiers), - Down => self.move_caret_down(modifiers), - - A => { - if modifiers.ctrl { - self.select_all() - } else { - Ok(()) - } - } - Home => self.move_caret_home(modifiers), - End => self.move_caret_end(modifiers), - _ => Ok(()), - } - } -} - #[derive(Debug)] pub struct EdModule<'a> { pub env: Env<'a>, diff --git a/editor/src/editor/mvc/ed_update.rs b/editor/src/editor/mvc/ed_update.rs new file mode 100644 index 0000000000..6e97e97bfa --- /dev/null +++ b/editor/src/editor/mvc/ed_update.rs @@ -0,0 +1,330 @@ +use crate::editor::code_lines::CodeLines; +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::ed_model::EdModel; +use crate::editor::syntax_highlight::HighlightStyle; +use crate::lang::ast::Expr2; +use crate::ui::text::caret_w_select::CaretWSelect; +use crate::ui::text::lines::MoveCaretFun; +use crate::ui::text::selection::validate_raw_sel; +use crate::ui::text::selection::RawSelection; +use crate::ui::text::selection::Selection; +use crate::ui::text::text_pos::TextPos; +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::no_mods; +use crate::window::keyboard_input::Modifiers; +use winit::event::VirtualKeyCode; +use VirtualKeyCode::*; + +impl<'a> EdModel<'a> { + pub fn move_caret( + &mut self, + move_fun: MoveCaretFun, + modifiers: &Modifiers, + ) -> UIResult<()> { + for caret_tup in self.caret_w_select_vec.iter_mut() { + caret_tup.0 = move_fun(&self.code_lines, caret_tup.0, modifiers)?; + caret_tup.1 = None; + } + + Ok(()) + } +} + +impl<'a> SelectableLines for EdModel<'a> { + fn get_caret(self) -> TextPos { + self.caret_w_select_vec.first().0.caret_pos + } + + // keeps active selection + fn set_caret(&mut self, caret_pos: TextPos) { + let caret_tup = self.caret_w_select_vec.first_mut(); + caret_tup.0.caret_pos = caret_pos; + caret_tup.1 = None; + } + + fn move_caret_left(&mut self, modifiers: &Modifiers) -> UIResult<()> { + let move_fun: MoveCaretFun = lines::move_caret_left; + EdModel::move_caret(self, move_fun, modifiers)?; + + Ok(()) + } + + fn move_caret_right(&mut self, modifiers: &Modifiers) -> UIResult<()> { + let move_fun: MoveCaretFun = lines::move_caret_right; + EdModel::move_caret(self, move_fun, modifiers)?; + + Ok(()) + } + + fn move_caret_up(&mut self, modifiers: &Modifiers) -> UIResult<()> { + let move_fun: MoveCaretFun = lines::move_caret_up; + EdModel::move_caret(self, move_fun, modifiers)?; + + Ok(()) + } + + fn move_caret_down(&mut self, modifiers: &Modifiers) -> UIResult<()> { + let move_fun: MoveCaretFun = lines::move_caret_down; + EdModel::move_caret(self, move_fun, modifiers)?; + + Ok(()) + } + + fn move_caret_home(&mut self, modifiers: &Modifiers) -> UIResult<()> { + let move_fun: MoveCaretFun = lines::move_caret_home; + EdModel::move_caret(self, move_fun, modifiers)?; + + Ok(()) + } + + fn move_caret_end(&mut self, modifiers: &Modifiers) -> UIResult<()> { + let move_fun: MoveCaretFun = lines::move_caret_end; + EdModel::move_caret(self, move_fun, modifiers)?; + + Ok(()) + } + + fn get_selection(&self) -> Option { + self.caret_w_select_vec.first().0.selection_opt + } + + fn is_selection_active(&self) -> bool { + self.get_selection().is_some() + } + + fn get_selected_str(&self) -> UIResult> { + if let Some(selection) = self.get_selection() { + let start_line_index = selection.start_pos.line; + let start_col = selection.start_pos.column; + let end_line_index = selection.end_pos.line; + let end_col = selection.end_pos.column; + + if start_line_index == end_line_index { + let line_ref = self.code_lines.get_line(start_line_index)?; + + Ok(Some(line_ref[start_col..end_col].to_string())) + } else { + let full_str = String::new(); + + // TODO + Ok(Some(full_str)) + } + } else { + Ok(None) + } + } + + fn set_raw_sel(&mut self, raw_sel: RawSelection) -> UIResult<()> { + self.caret_w_select_vec.first_mut().0.selection_opt = Some(validate_raw_sel(raw_sel)?); + + Ok(()) + } + + fn set_sel_none(&mut self) { + self.caret_w_select_vec.first_mut().0.selection_opt = None; + } + + fn set_caret_w_sel(&mut self, caret_w_sel: CaretWSelect) { + self.caret_w_select_vec.first_mut().0 = caret_w_sel; + } + + fn select_all(&mut self) -> UIResult<()> { + if self.code_lines.nr_of_chars() > 0 { + let last_pos = self.last_text_pos()?; + + self.set_raw_sel(RawSelection { + start_pos: TextPos { line: 0, column: 0 }, + end_pos: last_pos, + })?; + + self.set_caret(last_pos); + } + + Ok(()) + } + + fn last_text_pos(&self) -> UIResult { + let nr_of_lines = self.code_lines.lines.len(); + let last_line_index = nr_of_lines - 1; + let last_line = self.code_lines.get_line(last_line_index)?; + + Ok(TextPos { + line: self.code_lines.lines.len() - 1, + column: last_line.len(), + }) + } + + fn handle_key_down( + &mut self, + modifiers: &Modifiers, + virtual_keycode: VirtualKeyCode, + ) -> UIResult<()> { + match virtual_keycode { + Left => self.move_caret_left(modifiers), + Up => self.move_caret_up(modifiers), + Right => self.move_caret_right(modifiers), + Down => self.move_caret_down(modifiers), + + A => { + if modifiers.ctrl { + self.select_all() + } else { + Ok(()) + } + } + Home => self.move_caret_home(modifiers), + End => self.move_caret_end(modifiers), + _ => Ok(()), + } + } +} + +pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult<()> { + // TODO set all selections to none + + match received_char { + '{' => { + // TODO get MarkupNode where caret is positioned using a mapping from (row,col) to MarkNodeId + let curr_mark_node_id = ed_model.markup_root_id; + 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); + ed_model.move_caret_right(&no_mods())?; + ed_model.move_caret_right(&no_mods())?; + } + } + '\u{8}' | '\u{7f}' => { + // On Linux, '\u{8}' is backspace, + // On macOS '\u{7f}'. + unimplemented!("TODO implement backspace") + } + ch if is_newline(ch) => { + unimplemented!("TODO implement newline") + } + '\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 + } + _ => {} + } + + 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/ed_view.rs b/editor/src/editor/mvc/ed_view.rs index baaff2d91b..13c2b2e750 100644 --- a/editor/src/editor/mvc/ed_view.rs +++ b/editor/src/editor/mvc/ed_view.rs @@ -3,7 +3,6 @@ use crate::editor::code_lines::CodeLines; use crate::editor::config::Config; use crate::editor::ed_error::EdResult; use crate::editor::render_ast::build_code_graphics; -use crate::editor::slow_pool::SlowPool; use crate::graphics::primitives::rect::Rect; use crate::ui::text::caret_w_select::make_caret_rect; use crate::ui::text::caret_w_select::CaretWSelect; @@ -18,17 +17,16 @@ pub fn model_to_wgpu<'a>( size: &PhysicalSize, txt_coords: Vector2, config: &Config, - markup_node_pool: &'a SlowPool, ) -> EdResult<(wgpu_glyph::Section<'a>, Vec)> { let glyph_dim_rect = ed_model.glyph_dim_rect_opt.context(MissingGlyphDims {})?; let (section, mut rects) = build_code_graphics( - markup_node_pool.get(ed_model.markup_root_id), + ed_model.markup_node_pool.get(ed_model.markup_root_id), size, txt_coords, config, glyph_dim_rect, - markup_node_pool, + &ed_model.markup_node_pool, )?; let mut all_code_string = String::new(); diff --git a/editor/src/editor/mvc/mod.rs b/editor/src/editor/mvc/mod.rs index b8b26ab0c2..af950775db 100644 --- a/editor/src/editor/mvc/mod.rs +++ b/editor/src/editor/mvc/mod.rs @@ -1,4 +1,5 @@ pub mod app_model; pub mod app_update; pub mod ed_model; +pub mod ed_update; pub mod ed_view; diff --git a/editor/src/editor/slow_pool.rs b/editor/src/editor/slow_pool.rs index 83ea17d694..17fad94d99 100644 --- a/editor/src/editor/slow_pool.rs +++ b/editor/src/editor/slow_pool.rs @@ -1,6 +1,6 @@ use crate::editor::markup::nodes::MarkupNode; -pub type SlowNodeId = usize; +pub type MarkNodeId = usize; #[derive(Debug)] pub struct SlowPool { @@ -12,7 +12,7 @@ impl SlowPool { SlowPool { nodes: Vec::new() } } - pub fn add(&mut self, node: MarkupNode) -> SlowNodeId { + pub fn add(&mut self, node: MarkupNode) -> MarkNodeId { let id = self.nodes.len(); self.nodes.push(node); @@ -20,13 +20,17 @@ impl SlowPool { id } - pub fn get(&self, node_id: usize) -> &MarkupNode { + pub fn get(&self, node_id: MarkNodeId) -> &MarkupNode { // unwrap because Pool doesn't return Result either self.nodes.get(node_id).unwrap() } - pub fn get_mut(&mut self, node_id: usize) -> &mut MarkupNode { + pub fn get_mut(&mut self, node_id: MarkNodeId) -> &mut MarkupNode { // unwrap because Pool doesn't return Result either self.nodes.get_mut(node_id).unwrap() } + + pub fn replace_node(&mut self, node_id: MarkNodeId, new_node: MarkupNode) { + self.nodes[node_id] = new_node; + } } From 6009501114dbbc4e01a7c4ae02ec6b11a27cc8f7 Mon Sep 17 00:00:00 2001 From: rvcas Date: Tue, 23 Mar 2021 22:46:48 -0400 Subject: [PATCH 10/32] tests(solve_expr): mismatch still not properly being reported --- compiler/solve/tests/solve_expr.rs | 6 +++--- compiler/unify/src/unify.rs | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index d777cabb43..6254a3c6ef 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -652,8 +652,8 @@ mod solve_expr { } #[test] - fn applied_tag_function_mismatch() { - infer_eq( + fn mismatch_applied_tag_function() { + infer_eq_without_problem( indoc!( r#" x : List [ Foo Str ] @@ -662,7 +662,7 @@ mod solve_expr { x "# ), - "", + "List [ Foo Str ]", ) } diff --git a/compiler/unify/src/unify.rs b/compiler/unify/src/unify.rs index 464ad0fe47..c7a221824c 100644 --- a/compiler/unify/src/unify.rs +++ b/compiler/unify/src/unify.rs @@ -1088,6 +1088,8 @@ fn unify_flat_type( let problems = unify_pool(subs, pool, new_tag_union_var, *ret); + dbg!(problems.clone()); + if problems.is_empty() { let desc = subs.get(ctx.second); subs.union(ctx.first, ctx.second, desc); From bcdc4223aa426261b1d5d2dc1b8e495da1691f11 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Wed, 24 Mar 2021 19:50:24 +0100 Subject: [PATCH 11/32] added line-column->node_id mapping, progress on inputting record --- editor/src/editor/ed_error.rs | 19 ++++-- editor/src/editor/grid_node_map.rs | 37 ++++++++++ editor/src/editor/main.rs | 38 +++++++---- editor/src/editor/markup/nodes.rs | 40 +++++++++++ editor/src/editor/mod.rs | 1 + editor/src/editor/mvc/ed_model.rs | 13 +++- editor/src/editor/mvc/ed_update.rs | 93 ++++++++++++++++++++++++-- editor/src/editor/mvc/ed_view.rs | 23 +++---- editor/src/editor/render_ast.rs | 12 ++-- editor/src/editor/util.rs | 17 +++++ editor/src/graphics/primitives/text.rs | 6 +- editor/src/ui/util.rs | 13 +++- 12 files changed, 264 insertions(+), 48 deletions(-) create mode 100644 editor/src/editor/grid_node_map.rs diff --git a/editor/src/editor/ed_error.rs b/editor/src/editor/ed_error.rs index 5d4aed53df..07075ea58a 100644 --- a/editor/src/editor/ed_error.rs +++ b/editor/src/editor/ed_error.rs @@ -34,15 +34,16 @@ pub enum EdError { #[snafu(display("GetContentOnNestedNode: tried to get string content from Nested MarkupNode. Can only get content from Text or Blank nodes."))] GetContentOnNestedNode { backtrace: Backtrace }, - #[snafu(display("KeyNotFound: key {} was not found in HashMap.", key_str,))] - KeyNotFound { - key_str: String, + #[snafu(display("IndexOfFailed: Element {} was not found in collection {}.", elt_str, collection_str))] + IndexOfFailed { + elt_str: String, + collection_str: String, backtrace: Backtrace, }, - #[snafu(display("NestedNodeWithoutChildren: tried to retrieve child from Nested MarkupNode with id {} but it had no children.", node_id))] - NestedNodeWithoutChildren { - node_id: MarkNodeId, + #[snafu(display("KeyNotFound: key {} was not found in HashMap.", key_str,))] + KeyNotFound { + key_str: String, backtrace: Backtrace, }, @@ -53,6 +54,12 @@ pub enum EdError { backtrace: Backtrace, }, + #[snafu(display("NestedNodeWithoutChildren: tried to retrieve child from Nested MarkupNode with id {} but it had no children.", node_id))] + NestedNodeWithoutChildren { + node_id: MarkNodeId, + backtrace: Backtrace, + }, + #[snafu(display("NodeWithoutAttributes: expected to have a node with attributes. This is a Nested MarkupNode, only Text and Blank nodes have attributes."))] NodeWithoutAttributes { backtrace: Backtrace }, diff --git a/editor/src/editor/grid_node_map.rs b/editor/src/editor/grid_node_map.rs new file mode 100644 index 0000000000..b7e9dc5476 --- /dev/null +++ b/editor/src/editor/grid_node_map.rs @@ -0,0 +1,37 @@ + +use crate::ui::text::text_pos::TextPos; +use crate::ui::util::{slice_get, slice_get_mut}; +use crate::ui::ui_error::UIResult; +use crate::editor::slow_pool::MarkNodeId; + + +#[derive(Debug)] +pub struct GridNodeMap { + pub lines: Vec> +} + +impl GridNodeMap { + pub fn new() -> GridNodeMap { + vec![vec![]] + } + + pub fn add_to_line(&mut self, line: usize, len: usize, node_id: MarkNodeId) -> UIResult<()> { + let mut line = slice_get_mut(line, &mut self.lines)?; + let mut new_cols_vec: Vec = std::iter::repeat(node_id).take(len).collect(); + + line.append(&mut new_cols_vec); + + Ok(()) + } + + pub fn new_line(&mut self) { + self.lines.push(vec![]) + } + + pub fn get_id_at_row_col(&self, caret_pos: TextPos) -> UIResult { + let line = slice_get(caret_pos.line, &self.lines)?; + let node_id = slice_get(caret_pos.column, line)?; + + Ok(*node_id) + } +} \ No newline at end of file diff --git a/editor/src/editor/main.rs b/editor/src/editor/main.rs index 7d2218eb6e..606e8f027a 100644 --- a/editor/src/editor/main.rs +++ b/editor/src/editor/main.rs @@ -1,3 +1,5 @@ +use crate::editor::mvc::ed_view; +use crate::editor::mvc::ed_view::RenderedWgpu; use super::keyboard_input; use super::style::CODE_TXT_XY; use crate::editor::ed_error::print_ui_err; @@ -186,6 +188,8 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box> { } }; + let mut rendered_wgpu_opt: Option = None; + let mut app_model = AppModel::init(ed_model_opt); let mut keyboard_modifiers = ModifiersState::empty(); @@ -290,28 +294,36 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box> { .output; if let Some(ref mut ed_model) = app_model.ed_model_opt { - // TODO only calculate if markup_root has changed - let text_and_rects_res = super::mvc::ed_view::model_to_wgpu( - ed_model, - &size, - CODE_TXT_XY.into(), - &config, - ); + if rendered_wgpu_opt.is_none() || ed_model.dirty { + let rendered_wgpu_res = + ed_view::model_to_wgpu( + ed_model, + &size, + CODE_TXT_XY.into(), + &config, + ); - match text_and_rects_res { - Ok((text_section, rects)) => { - glyph_brush.queue(text_section); + match rendered_wgpu_res { + Ok(rendered_wgpu) => { + rendered_wgpu_opt = Some(rendered_wgpu) + } + Err(e) => print_err(&e), + } + } + + if let Some(ref rendered_wgpu) = rendered_wgpu_opt { + let borrowed_text = rendered_wgpu.text.to_borrowed(); + + glyph_brush.queue(borrowed_text); draw_all_rects( - &rects, + &rendered_wgpu.rects, &mut encoder, &frame.view, &gpu_device, &rect_resources, &ed_theme, ) - } - Err(e) => print_err(&e), } } else { queue_no_file_text( diff --git a/editor/src/editor/markup/nodes.rs b/editor/src/editor/markup/nodes.rs index b25a1c3c51..2694712f04 100644 --- a/editor/src/editor/markup/nodes.rs +++ b/editor/src/editor/markup/nodes.rs @@ -1,3 +1,5 @@ +use crate::editor::ed_error::GetContentOnNestedNode; +use crate::editor::ed_error::EdResult; use super::attribute::Attributes; use crate::editor::slow_pool::MarkNodeId; use crate::editor::slow_pool::SlowPool; @@ -48,9 +50,47 @@ impl MarkupNode { } } + pub fn get_children_ids(&self) -> Vec { + match self { + MarkupNode::Nested { children_ids, .. } => *children_ids, + MarkupNode::Text { parent_id_opt, .. } => vec![], + MarkupNode::Blank { parent_id_opt, .. } => vec![], + } + } + + pub fn get_sibling_ids(&self, markup_node_pool: &SlowPool) -> Vec { + if let Some(parent_id) = self.get_parent_id_opt() { + let parent_node = markup_node_pool.get(parent_id); + + parent_node.get_children_ids() + } else { + vec![] + } + } + + // can't be &str, this creates borrowing issues + pub fn get_content(&self) -> EdResult { + match self { + MarkupNode::Nested { + .. + } => GetContentOnNestedNode {}.fail(), + MarkupNode::Text { + content, + .. + } => Ok(content.clone()), + MarkupNode::Blank { + .. + } => Ok(BLANK_PLACEHOLDER.to_owned()), + } + } + pub fn is_blank(&self) -> bool { matches!(self, MarkupNode::Blank { .. }) } + + pub fn is_nested(&self) -> bool { + matches!(self, MarkupNode::Nested { .. }) + } } fn get_string<'a>(env: &Env<'a>, pool_str: &PoolStr) -> String { diff --git a/editor/src/editor/mod.rs b/editor/src/editor/mod.rs index b06ae4fc99..34ad60ba7a 100644 --- a/editor/src/editor/mod.rs +++ b/editor/src/editor/mod.rs @@ -12,3 +12,4 @@ mod style; mod syntax_highlight; mod theme; mod util; +mod grid_node_map; diff --git a/editor/src/editor/mvc/ed_model.rs b/editor/src/editor/mvc/ed_model.rs index e7898a3ff9..8c274ba3eb 100644 --- a/editor/src/editor/mvc/ed_model.rs +++ b/editor/src/editor/mvc/ed_model.rs @@ -1,3 +1,4 @@ +use crate::editor::grid_node_map::GridNodeMap; use crate::editor::code_lines::CodeLines; use crate::editor::slow_pool::{MarkNodeId, SlowPool}; use crate::editor::syntax_highlight::HighlightStyle; @@ -23,12 +24,17 @@ pub struct EdModel<'a> { pub module: EdModule<'a>, pub file_path: &'a Path, pub code_lines: CodeLines, + // allows us to map window coordinates to MarkNodeId's + pub grid_node_map: GridNodeMap, pub markup_root_id: MarkNodeId, pub markup_node_pool: SlowPool, + // contains single char dimensions, used to calculate line height, column width... pub glyph_dim_rect_opt: Option, pub has_focus: bool, // Option: MarkupNode that corresponds to caret position, Option because this MarkNodeId is only calculated when it needs to be used. pub caret_w_select_vec: NonEmpty<(CaretWSelect, Option)>, + // EdModel is dirty if it has changed since the previous render. + pub dirty: bool, } pub fn init_model<'a>( @@ -65,15 +71,20 @@ pub fn init_model<'a>( temp_markup_root_id }; + let code_lines = EdModel::build_code_lines_from_markup(markup_root_id, &markup_node_pool)?; + let grid_node_map = EdModel::build_node_map_from_markup(markup_root_id, &markup_node_pool)?; + Ok(EdModel { module, file_path, - code_lines: CodeLines::from_str(code_str), + code_lines, + grid_node_map, markup_root_id, markup_node_pool, glyph_dim_rect_opt: None, has_focus: true, caret_w_select_vec: NonEmpty::new((CaretWSelect::default(), None)), + dirty: true }) } diff --git a/editor/src/editor/mvc/ed_update.rs b/editor/src/editor/mvc/ed_update.rs index 6e97e97bfa..484719a684 100644 --- a/editor/src/editor/mvc/ed_update.rs +++ b/editor/src/editor/mvc/ed_update.rs @@ -1,3 +1,7 @@ +use crate::editor::util::index_of; +use crate::editor::grid_node_map::GridNodeMap; +use crate::editor::slow_pool::SlowPool; +use crate::editor::slow_pool::MarkNodeId; use crate::editor::code_lines::CodeLines; use crate::editor::ed_error::EdResult; use crate::editor::markup::attribute::Attributes; @@ -15,7 +19,6 @@ use crate::ui::text::text_pos::TextPos; 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::no_mods; use crate::window::keyboard_input::Modifiers; use winit::event::VirtualKeyCode; use VirtualKeyCode::*; @@ -33,6 +36,66 @@ impl<'a> EdModel<'a> { Ok(()) } + + // TODO delete + // 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_right(&mut self) { + for caret_tup in self.caret_w_select_vec.iter_mut() { + caret_tup.0.caret_pos.column += 1; + caret_tup.1 = None; + } + } + + pub fn build_node_map_from_markup(markup_root_id: MarkNodeId, markup_node_pool: &SlowPool) -> EdResult { + let mut grid_node_map = GridNodeMap::new(); + + EdModel::build_grid_node_map(markup_root_id, &mut grid_node_map, markup_node_pool); + + Ok(grid_node_map) + } + + fn build_grid_node_map(node_id: MarkNodeId, grid_node_map: &mut GridNodeMap, markup_node_pool: &SlowPool) -> EdResult<()> { + let node = markup_node_pool.get(node_id); + + if node.is_nested() { + for child_id in node.get_children_ids() { + EdModel::build_grid_node_map(child_id, grid_node_map, markup_node_pool)?; + } + } else { + let node_content_str = node.get_content()?; + + grid_node_map.add_to_line(0, node_content_str.len(), node_id); + } + + Ok(()) + } + + pub fn build_code_lines_from_markup(markup_root_id: MarkNodeId, markup_node_pool: &SlowPool) -> EdResult { + let mut all_code_string = String::new(); + + EdModel::build_markup_string(markup_root_id, &mut all_code_string, markup_node_pool)?; + + let code_lines = CodeLines::from_str(&all_code_string); + + Ok(code_lines) + } + + fn build_markup_string(node_id: MarkNodeId, all_code_string: &mut String, markup_node_pool: &SlowPool) -> EdResult<()> { + let node = markup_node_pool.get(node_id); + + if node.is_nested() { + for child_id in node.get_children_ids() { + EdModel::build_markup_string(child_id, all_code_string, markup_node_pool)?; + } + } else { + let node_content_str = node.get_content()?; + + all_code_string.push_str(&node_content_str); + } + + Ok(()) + } } impl<'a> SelectableLines for EdModel<'a> { @@ -189,8 +252,9 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult match received_char { '{' => { - // TODO get MarkupNode where caret is positioned using a mapping from (row,col) to MarkNodeId - let curr_mark_node_id = ed_model.markup_root_id; + 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(); @@ -231,8 +295,10 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult if is_blank_node { mark_node_pool.replace_node(curr_mark_node_id, nested_node); - ed_model.move_caret_right(&no_mods())?; - ed_model.move_caret_right(&no_mods())?; + + for _ in 0..nodes::LEFT_ACCOLADE.len() { + ed_model.simple_move_carets_right(); + } } } '\u{8}' | '\u{7f}' => { @@ -253,7 +319,22 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult => { // chars that can be ignored } - _ => {} + 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 = 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); + + // TODO match, create new MarkupNode + ASTNode + } + } } Ok(()) diff --git a/editor/src/editor/mvc/ed_view.rs b/editor/src/editor/mvc/ed_view.rs index 13c2b2e750..845f107277 100644 --- a/editor/src/editor/mvc/ed_view.rs +++ b/editor/src/editor/mvc/ed_view.rs @@ -1,5 +1,5 @@ + use super::ed_model::EdModel; -use crate::editor::code_lines::CodeLines; use crate::editor::config::Config; use crate::editor::ed_error::EdResult; use crate::editor::render_ast::build_code_graphics; @@ -11,13 +11,20 @@ use cgmath::Vector2; use snafu::OptionExt; use winit::dpi::PhysicalSize; + +#[derive(Debug)] +pub struct RenderedWgpu { + pub text: glyph_brush::OwnedSection, + pub rects: Vec +} + // create text and rectangles based on EdModel's markup_root pub fn model_to_wgpu<'a>( ed_model: &'a mut EdModel, size: &PhysicalSize, txt_coords: Vector2, config: &Config, -) -> EdResult<(wgpu_glyph::Section<'a>, Vec)> { +) -> EdResult { let glyph_dim_rect = ed_model.glyph_dim_rect_opt.context(MissingGlyphDims {})?; let (section, mut rects) = build_code_graphics( @@ -29,14 +36,6 @@ pub fn model_to_wgpu<'a>( &ed_model.markup_node_pool, )?; - let mut all_code_string = String::new(); - - for txt in section.text.iter() { - all_code_string.push_str(txt.text); - } - - ed_model.code_lines = CodeLines::from_str(&all_code_string); - let caret_w_sel_vec = ed_model .caret_w_select_vec .iter() @@ -48,7 +47,7 @@ pub fn model_to_wgpu<'a>( rects.append(&mut sel_rects); - Ok((section, rects)) + Ok(RenderedWgpu{text: section, rects}) } pub fn build_selection_graphics( @@ -67,7 +66,7 @@ pub fn build_selection_graphics( let top_left_x = txt_coords.x + caret_col * char_width; - let top_left_y = txt_coords.y + caret_row * char_height; + let top_left_y = txt_coords.y + caret_row * char_height + 0.1 * char_height; rects.push(make_caret_rect( top_left_x, diff --git a/editor/src/editor/render_ast.rs b/editor/src/editor/render_ast.rs index f1e26786c7..1ed46fbb4c 100644 --- a/editor/src/editor/render_ast.rs +++ b/editor/src/editor/render_ast.rs @@ -15,7 +15,7 @@ pub fn build_code_graphics<'a>( config: &Config, glyph_dim_rect: Rect, markup_node_pool: &'a SlowPool, -) -> EdResult<(wgpu_glyph::Section<'a>, Vec)> { +) -> EdResult<(glyph_brush::OwnedSection, Vec)> { let area_bounds = (size.width as f32, size.height as f32); let layout = wgpu_glyph::Layout::default().h_align(wgpu_glyph::HorizontalAlign::Left); @@ -47,8 +47,8 @@ fn markup_to_wgpu<'a>( markup_node: &'a MarkupNode, code_style: &CodeStyle, markup_node_pool: &'a SlowPool, -) -> EdResult<(Vec>, Vec)> { - let mut wgpu_texts: Vec> = Vec::new(); +) -> EdResult<(Vec, Vec)> { + let mut wgpu_texts: Vec = Vec::new(); let mut rects: Vec = Vec::new(); let mut txt_row_col = (0, 0); @@ -68,7 +68,7 @@ fn markup_to_wgpu<'a>( // TODO use text_row fn markup_to_wgpu_helper<'a>( markup_node: &'a MarkupNode, - wgpu_texts: &mut Vec>, + wgpu_texts: &mut Vec, rects: &mut Vec, code_style: &CodeStyle, txt_row_col: &mut (usize, usize), @@ -101,7 +101,7 @@ fn markup_to_wgpu_helper<'a>( } => { let highlight_color = map_get(&code_style.ed_theme.syntax_high_map, &syn_high_style)?; - let glyph_text = wgpu_glyph::Text::new(&content) + let glyph_text = glyph_brush::OwnedText::new(content) .with_color(colors::to_slice(*highlight_color)) .with_scale(code_style.font_size); @@ -114,7 +114,7 @@ fn markup_to_wgpu_helper<'a>( syn_high_style, parent_id_opt: _, } => { - let glyph_text = wgpu_glyph::Text::new(BLANK_PLACEHOLDER) + let glyph_text = glyph_brush::OwnedText::new(BLANK_PLACEHOLDER) .with_color(colors::to_slice(colors::WHITE)) .with_scale(code_style.font_size); diff --git a/editor/src/editor/util.rs b/editor/src/editor/util.rs index 6138fd8442..ac18475c17 100644 --- a/editor/src/editor/util.rs +++ b/editor/src/editor/util.rs @@ -1,3 +1,4 @@ +use crate::editor::ed_error::IndexOfFailed; use super::ed_error::{EdResult, KeyNotFound}; use snafu::OptionExt; use std::collections::HashMap; @@ -13,3 +14,19 @@ pub fn map_get<'a, K: ::std::fmt::Debug + std::hash::Hash + std::cmp::Eq, V>( Ok(value) } + +pub fn index_of(elt:T, slice: &[T]) -> EdResult { + let index = slice.iter().position(|&slice_elt| slice_elt == elt).with_context( + || { + let elt_str = format!("{:?}", elt); + let collection_str = format!("{:?}", slice); + + IndexOfFailed { + elt_str, + collection_str, + } + } + )?; + + Ok(index) +} diff --git a/editor/src/graphics/primitives/text.rs b/editor/src/graphics/primitives/text.rs index 6b25727304..6fde74d522 100644 --- a/editor/src/graphics/primitives/text.rs +++ b/editor/src/graphics/primitives/text.rs @@ -84,12 +84,12 @@ fn section_from_text<'a>( } pub fn section_from_glyph_text( - text: Vec, + text: Vec, screen_position: (f32, f32), area_bounds: (f32, f32), layout: wgpu_glyph::Layout, -) -> wgpu_glyph::Section { - Section { +) -> glyph_brush::OwnedSection { + glyph_brush::OwnedSection { screen_position, bounds: area_bounds, layout, diff --git a/editor/src/ui/util.rs b/editor/src/ui/util.rs index cf0636112d..fa40eade5c 100644 --- a/editor/src/ui/util.rs +++ b/editor/src/ui/util.rs @@ -8,7 +8,7 @@ pub fn is_newline(char_ref: &char) -> bool { newline_codes.contains(char_ref) } -// replace vec method that return Option with one that return Result and proper Error +// replace slice method that return Option with one that return Result and proper Error pub fn slice_get(index: usize, slice: &[T]) -> UIResult<&>::Output> { let elt_ref = slice.get(index).context(OutOfBounds { index, @@ -18,3 +18,14 @@ pub fn slice_get(index: usize, slice: &[T]) -> UIResult<&(index: usize, slice: &mut [T]) -> UIResult<&mut >::Output> { + let elt_ref = slice.get_mut(index).context(OutOfBounds { + index, + collection_name: "Slice", + len: slice.len(), + })?; + + Ok(elt_ref) +} + From 6aaa12079d8128d3ec269b5c94748627c963e852 Mon Sep 17 00:00:00 2001 From: rvcas Date: Wed, 24 Mar 2021 15:36:52 -0400 Subject: [PATCH 12/32] tests(test_reporting): properly testing a mismatch for applied tag functions --- compiler/reporting/tests/test_reporting.rs | 33 ++++++++++++++++++++++ compiler/solve/tests/solve_expr.rs | 7 +++-- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index ee2af9e6bf..f972187cce 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -5984,4 +5984,37 @@ mod test_reporting { ), ) } + + #[test] + fn applied_tag_function() { + report_problem_as( + indoc!( + r#" + x : List [ Foo Str ] + x = List.map [ 1, 2 ] Foo + + x + "# + ), + indoc!( + r#" + ── TYPE MISMATCH ─────────────────────────────────────────────────────────────── + + Something is off with the body of the `x` definition: + + 1│ x : List [ Foo Str ] + 2│ x = List.map [ 1, 2 ] Foo + ^^^^^^^^^^^^^^^^^^^^^ + + This `map` call produces: + + List [ Foo Num a ] + + But the type annotation on `x` says it should be: + + List [ Foo Str ] + "# + ), + ) + } } diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index 6254a3c6ef..c495f68c5c 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -651,18 +651,19 @@ mod solve_expr { ) } + // Tests (TagUnion, Func) #[test] - fn mismatch_applied_tag_function() { + fn applied_tag_function_with_annotation() { infer_eq_without_problem( indoc!( r#" - x : List [ Foo Str ] + x : List [ Foo I64 ] x = List.map [ 1, 2 ] Foo x "# ), - "List [ Foo Str ]", + "List [ Foo I64 ]", ) } From f8a6cd6a74043321b68ed6843b5277e869d5199a Mon Sep 17 00:00:00 2001 From: rvcas Date: Wed, 24 Mar 2021 15:37:23 -0400 Subject: [PATCH 13/32] feat(unify): clean up with @folkertdev --- compiler/unify/src/unify.rs | 40 ++++++++++++------------------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/compiler/unify/src/unify.rs b/compiler/unify/src/unify.rs index c7a221824c..258e044a41 100644 --- a/compiler/unify/src/unify.rs +++ b/compiler/unify/src/unify.rs @@ -1,4 +1,6 @@ -use roc_collections::all::{get_shared, relative_complement, union, MutMap, SendSet}; +use roc_collections::all::{ + default_hasher, get_shared, relative_complement, union, MutMap, SendSet, +}; use roc_module::ident::{Lowercase, TagName}; use roc_module::symbol::Symbol; use roc_types::boolean_algebra::Bool; @@ -1070,17 +1072,12 @@ fn unify_flat_type( } } (TagUnion(tags, ext), Func(args, closure, ret)) if tags.len() == 1 => { - let (tag, payload) = tags.iter().next().unwrap(); + let (tag_name, payload) = tags.iter().next().unwrap(); if payload.is_empty() { - let mut new_payload = vec![]; - let mut new_tags = MutMap::default(); + let mut new_tags = MutMap::with_capacity_and_hasher(1, default_hasher()); - for arg in args { - new_payload.push(*arg); - } - - new_tags.insert(tag.clone(), new_payload); + new_tags.insert(tag_name.clone(), args.clone()); let content = Structure(TagUnion(new_tags, *ext)); @@ -1088,16 +1085,12 @@ fn unify_flat_type( let problems = unify_pool(subs, pool, new_tag_union_var, *ret); - dbg!(problems.clone()); - if problems.is_empty() { let desc = subs.get(ctx.second); subs.union(ctx.first, ctx.second, desc); - - vec![] - } else { - problems } + + problems } else { mismatch!( "Trying to unify two flat types that are incompatible: {:?} ~ {:?}", @@ -1107,17 +1100,12 @@ fn unify_flat_type( } } (Func(args, closure, ret), TagUnion(tags, ext)) if tags.len() == 1 => { - let (tag, payload) = tags.iter().next().unwrap(); + let (tag_name, payload) = tags.iter().next().unwrap(); if payload.is_empty() { - let mut new_payload = vec![]; - let mut new_tags = MutMap::default(); + let mut new_tags = MutMap::with_capacity_and_hasher(1, default_hasher()); - for arg in args { - new_payload.push(*arg); - } - - new_tags.insert(tag.clone(), new_payload); + new_tags.insert(tag_name.clone(), args.clone()); let content = Structure(TagUnion(new_tags, *ext)); @@ -1128,11 +1116,9 @@ fn unify_flat_type( if problems.is_empty() { let desc = subs.get(ctx.first); subs.union(ctx.first, ctx.second, desc); - - vec![] - } else { - problems } + + problems } else { mismatch!( "Trying to unify two flat types that are incompatible: {:?} ~ {:?}", From 697b257ead18c58b8634ff24026de2ec1d5f6fef Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Fri, 26 Mar 2021 18:11:23 +0100 Subject: [PATCH 14/32] stuck on pool borrow error --- editor/src/editor/ed_error.rs | 6 +++ editor/src/editor/grid_node_map.rs | 12 +++-- editor/src/editor/markup/nodes.rs | 29 +++++++++-- editor/src/editor/mvc/ed_model.rs | 30 +++++++---- editor/src/editor/mvc/ed_update.rs | 80 +++++++++++++++++++++++++++-- editor/src/editor/util.rs | 2 +- editor/src/lang/ast.rs | 1 + editor/src/ui/text/big_text_area.rs | 2 +- editor/src/ui/text/lines.rs | 2 +- editor/src/ui/util.rs | 4 +- 10 files changed, 142 insertions(+), 26 deletions(-) diff --git a/editor/src/editor/ed_error.rs b/editor/src/editor/ed_error.rs index 07075ea58a..3edf252f30 100644 --- a/editor/src/editor/ed_error.rs +++ b/editor/src/editor/ed_error.rs @@ -54,6 +54,12 @@ pub enum EdError { backtrace: Backtrace, }, + #[snafu(display("NestedNodeRequired: required a Nested node at this position, node was a {}.", node_type))] + NestedNodeRequired { + node_type: String, + backtrace: Backtrace, + }, + #[snafu(display("NestedNodeWithoutChildren: tried to retrieve child from Nested MarkupNode with id {} but it had no children.", node_id))] NestedNodeWithoutChildren { node_id: MarkNodeId, diff --git a/editor/src/editor/grid_node_map.rs b/editor/src/editor/grid_node_map.rs index b7e9dc5476..afa83640c3 100644 --- a/editor/src/editor/grid_node_map.rs +++ b/editor/src/editor/grid_node_map.rs @@ -12,21 +12,23 @@ pub struct GridNodeMap { impl GridNodeMap { pub fn new() -> GridNodeMap { - vec![vec![]] + GridNodeMap { + lines: vec![vec![]], + } } pub fn add_to_line(&mut self, line: usize, len: usize, node_id: MarkNodeId) -> UIResult<()> { - let mut line = slice_get_mut(line, &mut self.lines)?; + let line_ref = slice_get_mut(line, &mut self.lines)?; let mut new_cols_vec: Vec = std::iter::repeat(node_id).take(len).collect(); - line.append(&mut new_cols_vec); + line_ref.append(&mut new_cols_vec); Ok(()) } - pub fn new_line(&mut self) { + /*pub fn new_line(&mut self) { self.lines.push(vec![]) - } + }*/ pub fn get_id_at_row_col(&self, caret_pos: TextPos) -> UIResult { let line = slice_get(caret_pos.line, &self.lines)?; diff --git a/editor/src/editor/markup/nodes.rs b/editor/src/editor/markup/nodes.rs index 2694712f04..999ad4827b 100644 --- a/editor/src/editor/markup/nodes.rs +++ b/editor/src/editor/markup/nodes.rs @@ -1,3 +1,4 @@ +use crate::editor::ed_error::NestedNodeRequired; use crate::editor::ed_error::GetContentOnNestedNode; use crate::editor::ed_error::EdResult; use super::attribute::Attributes; @@ -52,9 +53,9 @@ impl MarkupNode { pub fn get_children_ids(&self) -> Vec { match self { - MarkupNode::Nested { children_ids, .. } => *children_ids, - MarkupNode::Text { parent_id_opt, .. } => vec![], - MarkupNode::Blank { parent_id_opt, .. } => vec![], + MarkupNode::Nested { children_ids, .. } => children_ids.to_vec(), + MarkupNode::Text { .. } => vec![], + MarkupNode::Blank { .. } => vec![], } } @@ -84,6 +85,28 @@ impl MarkupNode { } } + pub fn add_child_at_index(&mut self, index: usize, child_id: MarkNodeId) -> EdResult<()> { + if let MarkupNode::Nested { children_ids, .. } = self { + children_ids.splice(index..index, vec![child_id]); + } else { + NestedNodeRequired { + node_type: self.node_type_as_string(), + }.fail()?; + } + + Ok(()) + } + + pub fn node_type_as_string(&self) -> String { + let type_str = match self { + MarkupNode::Nested { .. } => "Nested", + MarkupNode::Text { .. } => "Text", + MarkupNode::Blank { .. } => "Blank", + }; + + type_str.to_owned() + } + pub fn is_blank(&self) -> bool { matches!(self, MarkupNode::Blank { .. }) } diff --git a/editor/src/editor/mvc/ed_model.rs b/editor/src/editor/mvc/ed_model.rs index 8c274ba3eb..7e658c8749 100644 --- a/editor/src/editor/mvc/ed_model.rs +++ b/editor/src/editor/mvc/ed_model.rs @@ -1,3 +1,4 @@ +use crate::lang::pool::NodeId; use crate::editor::grid_node_map::GridNodeMap; use crate::editor::code_lines::CodeLines; use crate::editor::slow_pool::{MarkNodeId, SlowPool}; @@ -44,8 +45,8 @@ pub fn init_model<'a>( code_arena: &'a Bump, ) -> EdResult> { let mut module = EdModule::new(&code_str, env, code_arena)?; - // TODO fix moving issue and insert module.ast_root into pool - let ast_root_id = module.env.pool.add(Expr2::Blank); + + let ast_root_id = module.ast_root_id; let mut markup_node_pool = SlowPool::new(); let markup_root_id = if code_str.is_empty() { @@ -60,10 +61,12 @@ pub fn init_model<'a>( markup_node_pool.add(blank_root) } else { + let ast_root = &module.env.pool.get(ast_root_id); + let temp_markup_root_id = expr2_to_markup( code_arena, &mut module.env, - &module.ast_root, + ast_root, &mut markup_node_pool, ); set_parent_for_all(temp_markup_root_id, &mut markup_node_pool); @@ -91,7 +94,7 @@ pub fn init_model<'a>( #[derive(Debug)] pub struct EdModule<'a> { pub env: Env<'a>, - pub ast_root: Expr2, + pub ast_root_id: NodeId, } impl<'a> EdModule<'a> { @@ -104,18 +107,27 @@ impl<'a> EdModule<'a> { let expr2_result = str_to_expr2(&ast_arena, &code_str, &mut env, &mut scope, region); match expr2_result { - Ok((expr2, _output)) => Ok(EdModule { - env, - ast_root: expr2, - }), + Ok((expr2, _output)) => { + + let ast_root_id = env.pool.add(expr2); + + Ok( + EdModule { + env, + ast_root_id, + } + ) + }, Err(err) => Err(ParseError { syntax_err: format!("{:?}", err), }), } } else { + let ast_root_id = env.pool.add(Expr2::Blank); + Ok(EdModule { env, - ast_root: Expr2::Blank, + ast_root_id, }) } } diff --git a/editor/src/editor/mvc/ed_update.rs b/editor/src/editor/mvc/ed_update.rs index 484719a684..a182a918ff 100644 --- a/editor/src/editor/mvc/ed_update.rs +++ b/editor/src/editor/mvc/ed_update.rs @@ -1,3 +1,5 @@ +use roc_types::subs::Variable; +use crate::lang::pool::{PoolVec, PoolStr, NodeId}; use crate::editor::util::index_of; use crate::editor::grid_node_map::GridNodeMap; use crate::editor::slow_pool::SlowPool; @@ -50,7 +52,7 @@ impl<'a> EdModel<'a> { pub fn build_node_map_from_markup(markup_root_id: MarkNodeId, markup_node_pool: &SlowPool) -> EdResult { let mut grid_node_map = GridNodeMap::new(); - EdModel::build_grid_node_map(markup_root_id, &mut grid_node_map, markup_node_pool); + EdModel::build_grid_node_map(markup_root_id, &mut grid_node_map, markup_node_pool)?; Ok(grid_node_map) } @@ -65,7 +67,7 @@ impl<'a> EdModel<'a> { } else { let node_content_str = node.get_content()?; - grid_node_map.add_to_line(0, node_content_str.len(), node_id); + grid_node_map.add_to_line(0, node_content_str.len(), node_id)?; } Ok(()) @@ -99,7 +101,7 @@ impl<'a> EdModel<'a> { } impl<'a> SelectableLines for EdModel<'a> { - fn get_caret(self) -> TextPos { + fn get_caret(&self) -> TextPos { self.caret_w_select_vec.first().0.caret_pos } @@ -299,6 +301,19 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult for _ in 0..nodes::LEFT_ACCOLADE.len() { ed_model.simple_move_carets_right(); } + + // update mapping from possible caret positions to MarkNodeId's + 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 + )?; } } '\u{8}' | '\u{7f}' => { @@ -326,13 +341,68 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult 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 pool = &mut ed_model.module.env.pool; + let ast_node_ref = pool.get_mut(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); + let new_child_index = index_of(curr_mark_node_id, &sibling_ids)? + 1; - // TODO match, create new MarkupNode + ASTNode + match ast_node_ref { + Expr2::EmptyRecord => { + if curr_mark_node.get_content()? == nodes::LEFT_ACCOLADE { + let record_field_node = MarkupNode::Text { + content: ch.to_string(), + ast_node_id, + syn_high_style: HighlightStyle::RecordField, + attributes: Attributes::new(), + parent_id_opt: Some(parent_id), + }; + + let record_field_node_id = ed_model.markup_node_pool.add(record_field_node); + + let parent = ed_model.markup_node_pool.get_mut(parent_id); + parent.add_child_at_index(new_child_index, record_field_node_id)?; + + ed_model.simple_move_carets_right(); + + 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_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) + ); + let field_val_id = ed_model.module.env.pool.add(field_val); + let first_field = (field_name, field_var, field_val_id); + + let fields = PoolVec::new( + vec![first_field].into_iter(), + &mut ed_model.module.env.pool, + ); + + let new_ast_node = + Expr2::Record { + record_var, + fields, + }; + + ed_model.module.env.pool.set(ast_node_id, new_ast_node); + } + }, + Expr2::Record { record_var:_, fields } => { + if new_child_index == 3 { + let first_field = fields.iter_mut(pool).take(1); + + for field in first_field { + + } + } + }, + _ => (), + } } } } diff --git a/editor/src/editor/util.rs b/editor/src/editor/util.rs index ac18475c17..7be1dad3fe 100644 --- a/editor/src/editor/util.rs +++ b/editor/src/editor/util.rs @@ -16,7 +16,7 @@ pub fn map_get<'a, K: ::std::fmt::Debug + std::hash::Hash + std::cmp::Eq, V>( } pub fn index_of(elt:T, slice: &[T]) -> EdResult { - let index = slice.iter().position(|&slice_elt| slice_elt == elt).with_context( + let index = slice.iter().position(|slice_elt| *slice_elt == elt).with_context( || { let elt_str = format!("{:?}", elt); let collection_str = format!("{:?}", slice); diff --git a/editor/src/lang/ast.rs b/editor/src/lang/ast.rs index f06244691a..0ce662853e 100644 --- a/editor/src/lang/ast.rs +++ b/editor/src/lang/ast.rs @@ -99,6 +99,7 @@ pub enum Expr2 { Str(PoolStr), // 8B // Lookups Var(Symbol), // 8B + InvalidLookup(PoolStr), // 8B List { list_var: Variable, // 4B - required for uniqueness of the list diff --git a/editor/src/ui/text/big_text_area.rs b/editor/src/ui/text/big_text_area.rs index 22ba0f0da5..d8199aa4a7 100644 --- a/editor/src/ui/text/big_text_area.rs +++ b/editor/src/ui/text/big_text_area.rs @@ -119,7 +119,7 @@ impl Lines for BigTextArea { } impl SelectableLines for BigTextArea { - fn get_caret(self) -> TextPos { + fn get_caret(&self) -> TextPos { self.caret_w_select.caret_pos } diff --git a/editor/src/ui/text/lines.rs b/editor/src/ui/text/lines.rs index 0ec1389667..bd6a88ad88 100644 --- a/editor/src/ui/text/lines.rs +++ b/editor/src/ui/text/lines.rs @@ -30,7 +30,7 @@ pub trait Lines { } pub trait SelectableLines { - fn get_caret(self) -> TextPos; + fn get_caret(&self) -> TextPos; fn set_caret(&mut self, caret_pos: TextPos); diff --git a/editor/src/ui/util.rs b/editor/src/ui/util.rs index fa40eade5c..d55e9a7639 100644 --- a/editor/src/ui/util.rs +++ b/editor/src/ui/util.rs @@ -20,10 +20,12 @@ pub fn slice_get(index: usize, slice: &[T]) -> UIResult<&(index: usize, slice: &mut [T]) -> UIResult<&mut >::Output> { + let slice_len = slice.len(); + let elt_ref = slice.get_mut(index).context(OutOfBounds { index, collection_name: "Slice", - len: slice.len(), + len: slice_len, })?; Ok(elt_ref) From 5d85ff8b075fb3ee86f3ae361e27f764e688de9d Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Sat, 27 Mar 2021 13:49:32 +0100 Subject: [PATCH 15/32] able to input one item record without value as described in #1046 --- editor/src/editor/ed_error.rs | 25 +++- editor/src/editor/grid_node_map.rs | 29 +++-- editor/src/editor/main.rs | 33 +++--- editor/src/editor/markup/nodes.rs | 44 ++++--- editor/src/editor/mod.rs | 2 +- editor/src/editor/mvc/ed_model.rs | 33 ++---- editor/src/editor/mvc/ed_update.rs | 178 +++++++++++++++++++++++++---- editor/src/editor/mvc/ed_view.rs | 9 +- editor/src/editor/render_ast.rs | 13 ++- editor/src/editor/util.rs | 13 ++- editor/src/lang/ast.rs | 2 +- editor/src/lang/pool.rs | 2 +- editor/src/ui/util.rs | 8 +- 13 files changed, 281 insertions(+), 110 deletions(-) diff --git a/editor/src/editor/ed_error.rs b/editor/src/editor/ed_error.rs index 3edf252f30..588169d61a 100644 --- a/editor/src/editor/ed_error.rs +++ b/editor/src/editor/ed_error.rs @@ -31,10 +31,25 @@ pub enum EdError { ))] ClipboardInitFailed { err_msg: String }, + #[snafu(display( + "ExpectedTextNode: the function {} expected a Text node, got {} instead.", + function_name, + node_type + ))] + ExpectedTextNode { + function_name: String, + node_type: String, + backtrace: Backtrace, + }, + #[snafu(display("GetContentOnNestedNode: tried to get string content from Nested MarkupNode. Can only get content from Text or Blank nodes."))] GetContentOnNestedNode { backtrace: Backtrace }, - #[snafu(display("IndexOfFailed: Element {} was not found in collection {}.", elt_str, collection_str))] + #[snafu(display( + "IndexOfFailed: Element {} was not found in collection {}.", + elt_str, + collection_str + ))] IndexOfFailed { elt_str: String, collection_str: String, @@ -54,7 +69,10 @@ pub enum EdError { backtrace: Backtrace, }, - #[snafu(display("NestedNodeRequired: required a Nested node at this position, node was a {}.", node_type))] + #[snafu(display( + "NestedNodeRequired: required a Nested node at this position, node was a {}.", + node_type + ))] NestedNodeRequired { node_type: String, backtrace: Backtrace, @@ -85,6 +103,9 @@ 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."))] + RecordWithoutFields { backtrace: Backtrace }, + #[snafu(display("UIError: {}", msg))] UIErrorBacktrace { msg: String, backtrace: Backtrace }, } diff --git a/editor/src/editor/grid_node_map.rs b/editor/src/editor/grid_node_map.rs index afa83640c3..f2455ecce0 100644 --- a/editor/src/editor/grid_node_map.rs +++ b/editor/src/editor/grid_node_map.rs @@ -1,13 +1,11 @@ - -use crate::ui::text::text_pos::TextPos; -use crate::ui::util::{slice_get, slice_get_mut}; -use crate::ui::ui_error::UIResult; use crate::editor::slow_pool::MarkNodeId; - +use crate::ui::text::text_pos::TextPos; +use crate::ui::ui_error::UIResult; +use crate::ui::util::{slice_get, slice_get_mut}; #[derive(Debug)] pub struct GridNodeMap { - pub lines: Vec> + pub lines: Vec>, } impl GridNodeMap { @@ -26,6 +24,21 @@ impl GridNodeMap { Ok(()) } + pub fn insert_between_line( + &mut self, + line: usize, + index: usize, + len: usize, + node_id: MarkNodeId, + ) -> UIResult<()> { + let line_ref = slice_get_mut(line, &mut self.lines)?; + let new_cols_vec: Vec = std::iter::repeat(node_id).take(len).collect(); + + line_ref.splice(index..index, new_cols_vec); + + Ok(()) + } + /*pub fn new_line(&mut self) { self.lines.push(vec![]) }*/ @@ -33,7 +46,7 @@ impl GridNodeMap { pub fn get_id_at_row_col(&self, caret_pos: TextPos) -> UIResult { let line = slice_get(caret_pos.line, &self.lines)?; let node_id = slice_get(caret_pos.column, line)?; - + Ok(*node_id) } -} \ No newline at end of file +} diff --git a/editor/src/editor/main.rs b/editor/src/editor/main.rs index 606e8f027a..8388e528d1 100644 --- a/editor/src/editor/main.rs +++ b/editor/src/editor/main.rs @@ -1,8 +1,8 @@ -use crate::editor::mvc::ed_view; -use crate::editor::mvc::ed_view::RenderedWgpu; use super::keyboard_input; use super::style::CODE_TXT_XY; use crate::editor::ed_error::print_ui_err; +use crate::editor::mvc::ed_view; +use crate::editor::mvc::ed_view::RenderedWgpu; use crate::editor::resources::strings::NOTHING_OPENED; use crate::editor::{ config::Config, @@ -296,17 +296,10 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box> { if let Some(ref mut ed_model) = app_model.ed_model_opt { if rendered_wgpu_opt.is_none() || ed_model.dirty { let rendered_wgpu_res = - ed_view::model_to_wgpu( - ed_model, - &size, - CODE_TXT_XY.into(), - &config, - ); + ed_view::model_to_wgpu(ed_model, &size, CODE_TXT_XY.into(), &config); match rendered_wgpu_res { - Ok(rendered_wgpu) => { - rendered_wgpu_opt = Some(rendered_wgpu) - } + Ok(rendered_wgpu) => rendered_wgpu_opt = Some(rendered_wgpu), Err(e) => print_err(&e), } } @@ -314,16 +307,16 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box> { if let Some(ref rendered_wgpu) = rendered_wgpu_opt { let borrowed_text = rendered_wgpu.text.to_borrowed(); - glyph_brush.queue(borrowed_text); + glyph_brush.queue(borrowed_text); - draw_all_rects( - &rendered_wgpu.rects, - &mut encoder, - &frame.view, - &gpu_device, - &rect_resources, - &ed_theme, - ) + draw_all_rects( + &rendered_wgpu.rects, + &mut encoder, + &frame.view, + &gpu_device, + &rect_resources, + &ed_theme, + ) } } else { queue_no_file_text( diff --git a/editor/src/editor/markup/nodes.rs b/editor/src/editor/markup/nodes.rs index 999ad4827b..021e76fce7 100644 --- a/editor/src/editor/markup/nodes.rs +++ b/editor/src/editor/markup/nodes.rs @@ -1,7 +1,8 @@ -use crate::editor::ed_error::NestedNodeRequired; -use crate::editor::ed_error::GetContentOnNestedNode; -use crate::editor::ed_error::EdResult; use super::attribute::Attributes; +use crate::editor::ed_error::EdResult; +use crate::editor::ed_error::ExpectedTextNode; +use crate::editor::ed_error::GetContentOnNestedNode; +use crate::editor::ed_error::NestedNodeRequired; use crate::editor::slow_pool::MarkNodeId; use crate::editor::slow_pool::SlowPool; use crate::editor::syntax_highlight::HighlightStyle; @@ -29,7 +30,7 @@ pub enum MarkupNode { Blank { ast_node_id: NodeId, attributes: Attributes, - syn_high_style: HighlightStyle, + syn_high_style: HighlightStyle, // TODO remove HighlightStyle, this is always HighlightStyle::Blank parent_id_opt: Option, }, } @@ -72,16 +73,25 @@ impl MarkupNode { // can't be &str, this creates borrowing issues pub fn get_content(&self) -> EdResult { match self { - MarkupNode::Nested { - .. - } => GetContentOnNestedNode {}.fail(), - MarkupNode::Text { - content, - .. - } => Ok(content.clone()), - MarkupNode::Blank { - .. - } => Ok(BLANK_PLACEHOLDER.to_owned()), + MarkupNode::Nested { .. } => GetContentOnNestedNode {}.fail(), + MarkupNode::Text { content, .. } => Ok(content.clone()), + MarkupNode::Blank { .. } => Ok(BLANK_PLACEHOLDER.to_owned()), + } + } + + pub fn get_content_mut(&mut self) -> EdResult<&mut String> { + match self { + MarkupNode::Nested { .. } => ExpectedTextNode { + function_name: "set_content".to_owned(), + node_type: self.node_type_as_string(), + } + .fail(), + MarkupNode::Text { content, .. } => Ok(content), + MarkupNode::Blank { .. } => ExpectedTextNode { + function_name: "set_content".to_owned(), + node_type: self.node_type_as_string(), + } + .fail(), } } @@ -91,7 +101,8 @@ impl MarkupNode { } else { NestedNodeRequired { node_type: self.node_type_as_string(), - }.fail()?; + } + .fail()?; } Ok(()) @@ -123,6 +134,7 @@ 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 COLON: &str = ": "; fn new_markup_node( text: String, @@ -246,7 +258,7 @@ pub fn expr2_to_markup<'a, 'b>( )); children_ids.push(new_markup_node( - ": ".to_string(), + COLON.to_string(), node_id, HighlightStyle::Operator, markup_node_pool, diff --git a/editor/src/editor/mod.rs b/editor/src/editor/mod.rs index 34ad60ba7a..c6e6e5aca6 100644 --- a/editor/src/editor/mod.rs +++ b/editor/src/editor/mod.rs @@ -1,6 +1,7 @@ mod code_lines; mod config; mod ed_error; +mod grid_node_map; mod keyboard_input; pub mod main; mod markup; @@ -12,4 +13,3 @@ mod style; mod syntax_highlight; mod theme; mod util; -mod grid_node_map; diff --git a/editor/src/editor/mvc/ed_model.rs b/editor/src/editor/mvc/ed_model.rs index 7e658c8749..d59a4569a0 100644 --- a/editor/src/editor/mvc/ed_model.rs +++ b/editor/src/editor/mvc/ed_model.rs @@ -1,6 +1,5 @@ -use crate::lang::pool::NodeId; -use crate::editor::grid_node_map::GridNodeMap; use crate::editor::code_lines::CodeLines; +use crate::editor::grid_node_map::GridNodeMap; use crate::editor::slow_pool::{MarkNodeId, SlowPool}; use crate::editor::syntax_highlight::HighlightStyle; use crate::editor::{ @@ -12,6 +11,7 @@ use crate::editor::{ use crate::graphics::primitives::rect::Rect; use crate::lang::ast::Expr2; use crate::lang::expr::{str_to_expr2, Env}; +use crate::lang::pool::NodeId; use crate::lang::scope::Scope; use crate::ui::text::caret_w_select::CaretWSelect; use bumpalo::collections::String as BumpString; @@ -35,7 +35,7 @@ pub struct EdModel<'a> { // Option: MarkupNode that corresponds to caret position, Option because this MarkNodeId is only calculated when it needs to be used. pub caret_w_select_vec: NonEmpty<(CaretWSelect, Option)>, // EdModel is dirty if it has changed since the previous render. - pub dirty: bool, + pub dirty: bool, } pub fn init_model<'a>( @@ -63,12 +63,8 @@ pub fn init_model<'a>( } else { let ast_root = &module.env.pool.get(ast_root_id); - let temp_markup_root_id = expr2_to_markup( - code_arena, - &mut module.env, - ast_root, - &mut markup_node_pool, - ); + let temp_markup_root_id = + expr2_to_markup(code_arena, &mut module.env, ast_root, &mut markup_node_pool); set_parent_for_all(temp_markup_root_id, &mut markup_node_pool); temp_markup_root_id @@ -87,7 +83,7 @@ pub fn init_model<'a>( glyph_dim_rect_opt: None, has_focus: true, caret_w_select_vec: NonEmpty::new((CaretWSelect::default(), None)), - dirty: true + dirty: true, }) } @@ -108,16 +104,10 @@ impl<'a> EdModule<'a> { match expr2_result { Ok((expr2, _output)) => { - let ast_root_id = env.pool.add(expr2); - - Ok( - EdModule { - env, - ast_root_id, - } - ) - }, + + Ok(EdModule { env, ast_root_id }) + } Err(err) => Err(ParseError { syntax_err: format!("{:?}", err), }), @@ -125,10 +115,7 @@ impl<'a> EdModule<'a> { } else { let ast_root_id = env.pool.add(Expr2::Blank); - Ok(EdModule { - env, - ast_root_id, - }) + Ok(EdModule { env, ast_root_id }) } } } diff --git a/editor/src/editor/mvc/ed_update.rs b/editor/src/editor/mvc/ed_update.rs index a182a918ff..a70811ad09 100644 --- a/editor/src/editor/mvc/ed_update.rs +++ b/editor/src/editor/mvc/ed_update.rs @@ -1,17 +1,17 @@ -use roc_types::subs::Variable; -use crate::lang::pool::{PoolVec, PoolStr, NodeId}; -use crate::editor::util::index_of; -use crate::editor::grid_node_map::GridNodeMap; -use crate::editor::slow_pool::SlowPool; -use crate::editor::slow_pool::MarkNodeId; 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::slow_pool::MarkNodeId; +use crate::editor::slow_pool::SlowPool; use crate::editor::syntax_highlight::HighlightStyle; +use crate::editor::util::index_of; use crate::lang::ast::Expr2; +use crate::lang::pool::{NodeId, PoolStr, PoolVec}; use crate::ui::text::caret_w_select::CaretWSelect; use crate::ui::text::lines::MoveCaretFun; use crate::ui::text::selection::validate_raw_sel; @@ -22,6 +22,8 @@ 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::*; @@ -49,7 +51,10 @@ impl<'a> EdModel<'a> { } } - pub fn build_node_map_from_markup(markup_root_id: MarkNodeId, markup_node_pool: &SlowPool) -> EdResult { + pub fn build_node_map_from_markup( + markup_root_id: MarkNodeId, + markup_node_pool: &SlowPool, + ) -> EdResult { let mut grid_node_map = GridNodeMap::new(); EdModel::build_grid_node_map(markup_root_id, &mut grid_node_map, markup_node_pool)?; @@ -57,7 +62,11 @@ impl<'a> EdModel<'a> { Ok(grid_node_map) } - fn build_grid_node_map(node_id: MarkNodeId, grid_node_map: &mut GridNodeMap, markup_node_pool: &SlowPool) -> EdResult<()> { + fn build_grid_node_map( + node_id: MarkNodeId, + grid_node_map: &mut GridNodeMap, + markup_node_pool: &SlowPool, + ) -> EdResult<()> { let node = markup_node_pool.get(node_id); if node.is_nested() { @@ -73,17 +82,24 @@ impl<'a> EdModel<'a> { Ok(()) } - pub fn build_code_lines_from_markup(markup_root_id: MarkNodeId, markup_node_pool: &SlowPool) -> EdResult { + pub fn build_code_lines_from_markup( + markup_root_id: MarkNodeId, + markup_node_pool: &SlowPool, + ) -> EdResult { let mut all_code_string = String::new(); EdModel::build_markup_string(markup_root_id, &mut all_code_string, markup_node_pool)?; - + let code_lines = CodeLines::from_str(&all_code_string); Ok(code_lines) } - fn build_markup_string(node_id: MarkNodeId, all_code_string: &mut String, markup_node_pool: &SlowPool) -> EdResult<()> { + fn build_markup_string( + node_id: MarkNodeId, + all_code_string: &mut String, + markup_node_pool: &SlowPool, + ) -> EdResult<()> { let node = markup_node_pool.get(node_id); if node.is_nested() { @@ -251,6 +267,7 @@ impl<'a> SelectableLines for EdModel<'a> { 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 { '{' => { @@ -316,6 +333,67 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut 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(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(); + } + + // TODO update grid_node_map + + // TODO update AST node + } + other => unimplemented!("TODO implement updating of Expr2 {:?}.", other) + } + } + } '\u{8}' | '\u{7f}' => { // On Linux, '\u{8}' is backspace, // On macOS '\u{7f}'. @@ -341,9 +419,8 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult 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 pool = &mut ed_model.module.env.pool; - let ast_node_ref = pool.get_mut(ast_node_id); - + let ast_node_ref = ed_model.module.env.pool.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); @@ -352,8 +429,12 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult match ast_node_ref { Expr2::EmptyRecord => { if curr_mark_node.get_content()? == nodes::LEFT_ACCOLADE { + + // update Markup + let record_field_str = &ch.to_string(); + let record_field_node = MarkupNode::Text { - content: ch.to_string(), + content: record_field_str.to_owned(), ast_node_id, syn_high_style: HighlightStyle::RecordField, attributes: Attributes::new(), @@ -365,8 +446,18 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult let parent = ed_model.markup_node_pool.get_mut(parent_id); parent.add_child_at_index(new_child_index, record_field_node_id)?; + // update caret ed_model.simple_move_carets_right(); + // update GridNodeMap + ed_model.grid_node_map.insert_between_line( + old_caret_pos.line, + old_caret_pos.column + 1, + record_field_str.len(), + record_field_node_id, + )?; + + // 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); @@ -377,7 +468,7 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult ); let field_val_id = ed_model.module.env.pool.add(field_val); let first_field = (field_name, field_var, field_val_id); - + let fields = PoolVec::new( vec![first_field].into_iter(), &mut ed_model.module.env.pool, @@ -393,15 +484,62 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult } }, Expr2::Record { record_var:_, fields } => { - if new_child_index == 3 { - let first_field = fields.iter_mut(pool).take(1); + if new_child_index == 2 { - for field in first_field { + // 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 } }, - _ => (), + other => { + unimplemented!("TODO implement updating of Expr2 {:?}.", other) + }, } } } diff --git a/editor/src/editor/mvc/ed_view.rs b/editor/src/editor/mvc/ed_view.rs index 845f107277..014005e0fe 100644 --- a/editor/src/editor/mvc/ed_view.rs +++ b/editor/src/editor/mvc/ed_view.rs @@ -1,4 +1,3 @@ - use super::ed_model::EdModel; use crate::editor::config::Config; use crate::editor::ed_error::EdResult; @@ -11,11 +10,10 @@ use cgmath::Vector2; use snafu::OptionExt; use winit::dpi::PhysicalSize; - #[derive(Debug)] pub struct RenderedWgpu { pub text: glyph_brush::OwnedSection, - pub rects: Vec + pub rects: Vec, } // create text and rectangles based on EdModel's markup_root @@ -47,7 +45,10 @@ pub fn model_to_wgpu<'a>( rects.append(&mut sel_rects); - Ok(RenderedWgpu{text: section, rects}) + Ok(RenderedWgpu { + text: section, + rects, + }) } pub fn build_selection_graphics( diff --git a/editor/src/editor/render_ast.rs b/editor/src/editor/render_ast.rs index 1ed46fbb4c..b6300dadf6 100644 --- a/editor/src/editor/render_ast.rs +++ b/editor/src/editor/render_ast.rs @@ -120,16 +120,19 @@ fn markup_to_wgpu_helper<'a>( let highlight_color = map_get(&code_style.ed_theme.syntax_high_map, &syn_high_style)?; + let char_width = code_style.glyph_dim_rect.width; + let char_height = code_style.glyph_dim_rect.height; + let hole_rect = Rect { top_left_coords: ( - code_style.txt_coords.x - + (txt_row_col.0 as f32) * code_style.glyph_dim_rect.height, + code_style.txt_coords.x + (txt_row_col.1 as f32) * char_width, code_style.txt_coords.y - + (txt_row_col.1 as f32) * code_style.glyph_dim_rect.width, + + (txt_row_col.0 as f32) * char_height + + 0.1 * char_height, ) .into(), - width: code_style.glyph_dim_rect.width, - height: code_style.glyph_dim_rect.height, + width: char_width, + height: char_height, color: *highlight_color, }; rects.push(hole_rect); diff --git a/editor/src/editor/util.rs b/editor/src/editor/util.rs index 7be1dad3fe..f0690adcbd 100644 --- a/editor/src/editor/util.rs +++ b/editor/src/editor/util.rs @@ -1,5 +1,5 @@ -use crate::editor::ed_error::IndexOfFailed; use super::ed_error::{EdResult, KeyNotFound}; +use crate::editor::ed_error::IndexOfFailed; use snafu::OptionExt; use std::collections::HashMap; @@ -15,9 +15,11 @@ pub fn map_get<'a, K: ::std::fmt::Debug + std::hash::Hash + std::cmp::Eq, V>( Ok(value) } -pub fn index_of(elt:T, slice: &[T]) -> EdResult { - let index = slice.iter().position(|slice_elt| *slice_elt == elt).with_context( - || { +pub fn index_of(elt: T, slice: &[T]) -> EdResult { + let index = slice + .iter() + .position(|slice_elt| *slice_elt == elt) + .with_context(|| { let elt_str = format!("{:?}", elt); let collection_str = format!("{:?}", slice); @@ -25,8 +27,7 @@ pub fn index_of(elt:T, slice: &[T]) -> EdRe elt_str, collection_str, } - } - )?; + })?; Ok(index) } diff --git a/editor/src/lang/ast.rs b/editor/src/lang/ast.rs index 0ce662853e..b415d1bf9d 100644 --- a/editor/src/lang/ast.rs +++ b/editor/src/lang/ast.rs @@ -98,7 +98,7 @@ pub enum Expr2 { /// string literals of length 31B or more Str(PoolStr), // 8B // Lookups - Var(Symbol), // 8B + Var(Symbol), // 8B InvalidLookup(PoolStr), // 8B List { diff --git a/editor/src/lang/pool.rs b/editor/src/lang/pool.rs index bd3d6bc35d..1d49b0946f 100644 --- a/editor/src/lang/pool.rs +++ b/editor/src/lang/pool.rs @@ -352,7 +352,7 @@ impl<'a, T: 'a + Sized> PoolVec { self.pool_list_iter(pool) } - pub fn iter_mut(&mut self, pool: &'a Pool) -> impl ExactSizeIterator { + pub fn iter_mut(&self, pool: &'a mut Pool) -> impl ExactSizeIterator { self.pool_list_iter_mut(pool) } diff --git a/editor/src/ui/util.rs b/editor/src/ui/util.rs index d55e9a7639..d33d8da38e 100644 --- a/editor/src/ui/util.rs +++ b/editor/src/ui/util.rs @@ -19,9 +19,12 @@ pub fn slice_get(index: usize, slice: &[T]) -> UIResult<&(index: usize, slice: &mut [T]) -> UIResult<&mut >::Output> { +pub fn slice_get_mut( + index: usize, + slice: &mut [T], +) -> UIResult<&mut >::Output> { let slice_len = slice.len(); - + let elt_ref = slice.get_mut(index).context(OutOfBounds { index, collection_name: "Slice", @@ -30,4 +33,3 @@ pub fn slice_get_mut(index: usize, slice: &mut [T]) -> UIResult<&mut Date: Sat, 27 Mar 2021 15:12:00 +0100 Subject: [PATCH 16/32] added adjustment of grid_node_map and AST node when colon is entered --- editor/src/editor/mvc/ed_update.rs | 31 ++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/editor/src/editor/mvc/ed_update.rs b/editor/src/editor/mvc/ed_update.rs index a70811ad09..7a527a412f 100644 --- a/editor/src/editor/mvc/ed_update.rs +++ b/editor/src/editor/mvc/ed_update.rs @@ -319,7 +319,7 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult ed_model.simple_move_carets_right(); } - // update mapping from possible caret positions to MarkNodeId's + // update GridNodeMap ed_model.grid_node_map.add_to_line( old_caret_pos.line, nodes::LEFT_ACCOLADE.len(), @@ -349,7 +349,7 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult let ast_node_ref = ed_model.module.env.pool.get(ast_node_id); match ast_node_ref { - Expr2::Record { record_var:_, fields:_ } => { + Expr2::Record { record_var:_, fields } => { // update Markup let record_colon = nodes::COLON; @@ -386,9 +386,32 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult ed_model.simple_move_carets_right(); } - // TODO update grid_node_map + // update GridNodeMap + ed_model.grid_node_map.add_to_line( + old_caret_pos.line, + nodes::COLON.len(), + record_colon_node_id, + )?; - // TODO update AST node + 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) } From 0dc2e1780be3136f0076e3cbaffc2c2710c36f9b Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Sat, 27 Mar 2021 18:23:34 +0100 Subject: [PATCH 17/32] cleanup of update on new input --- editor/src/editor/ed_error.rs | 9 + editor/src/editor/mvc/ed_update.rs | 319 ++++--------------------- editor/src/editor/mvc/mod.rs | 1 + editor/src/editor/mvc/record_update.rs | 233 ++++++++++++++++++ 4 files changed, 289 insertions(+), 273 deletions(-) create mode 100644 editor/src/editor/mvc/record_update.rs 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() + } +} From fe9e9af5762725d29799b3ba142d73b9e45388b7 Mon Sep 17 00:00:00 2001 From: rvcas Date: Sat, 27 Mar 2021 18:00:36 -0400 Subject: [PATCH 18/32] tests(test_gen): add tests for mono --- compiler/mono/src/ir.rs | 10 ++++++++++ compiler/mono/src/layout.rs | 1 + compiler/test_gen/src/gen_list.rs | 16 ++++++++++++++++ 3 files changed, 27 insertions(+) diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index c1e212b6e5..2e954f346b 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -2802,6 +2802,16 @@ pub fn with_hole<'a>( use crate::layout::UnionVariant::*; let arena = env.arena; + match layout_cache.from_var(arena, variant_var, env.subs) { + Err(e) => panic!("invalid layout {:?}", e), + Ok(Layout::FunctionPointer(argument_layouts, ret_layout)) => { + dbg!(argument_layouts, ret_layout); + } + Ok(_) => { + panic!("idk") + } + } + let res_variant = crate::layout::union_sorted_tags(env.arena, variant_var, env.subs); let variant = match res_variant { diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index 975b16d6f9..7cd65a7471 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -1,6 +1,7 @@ use crate::ir::Parens; use bumpalo::collections::Vec; use bumpalo::Bump; +use core::panic; use roc_collections::all::{default_hasher, MutMap, MutSet}; use roc_module::ident::{Lowercase, TagName}; use roc_module::symbol::{Interns, Symbol}; diff --git a/compiler/test_gen/src/gen_list.rs b/compiler/test_gen/src/gen_list.rs index 2ed0c4fbe4..a9cfe5465b 100644 --- a/compiler/test_gen/src/gen_list.rs +++ b/compiler/test_gen/src/gen_list.rs @@ -1803,3 +1803,19 @@ fn cleanup_because_exception() { RocList ); } + +#[test] +fn applied_tag_function() { + assert_evals_to!( + indoc!( + r#" + x : List [ Foo Str ] + x = List.map [ "a", "b" ] Foo + + x + "# + ), + RocList::from_slice(&[false; 1]), + RocList + ); +} From 48b1b62aaaecbd40c54b0996ae26eac5b9774990 Mon Sep 17 00:00:00 2001 From: rvcas Date: Sun, 28 Mar 2021 17:00:09 -0400 Subject: [PATCH 19/32] tests(test_gen): add get_tags tests for applied tag functions --- compiler/test_gen/src/gen_list.rs | 16 --------- compiler/test_gen/src/gen_tags.rs | 59 +++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 16 deletions(-) diff --git a/compiler/test_gen/src/gen_list.rs b/compiler/test_gen/src/gen_list.rs index a9cfe5465b..2ed0c4fbe4 100644 --- a/compiler/test_gen/src/gen_list.rs +++ b/compiler/test_gen/src/gen_list.rs @@ -1803,19 +1803,3 @@ fn cleanup_because_exception() { RocList ); } - -#[test] -fn applied_tag_function() { - assert_evals_to!( - indoc!( - r#" - x : List [ Foo Str ] - x = List.map [ "a", "b" ] Foo - - x - "# - ), - RocList::from_slice(&[false; 1]), - RocList - ); -} diff --git a/compiler/test_gen/src/gen_tags.rs b/compiler/test_gen/src/gen_tags.rs index c178ea5c85..a16ba1a6af 100644 --- a/compiler/test_gen/src/gen_tags.rs +++ b/compiler/test_gen/src/gen_tags.rs @@ -3,6 +3,7 @@ use crate::assert_evals_to; use crate::assert_llvm_evals_to; use indoc::indoc; +use roc_std::{RocList, RocStr}; #[test] fn applied_tag_nothing_ir() { @@ -974,3 +975,61 @@ fn newtype_wrapper() { |x: &i64| *x ); } + +#[test] +fn applied_tag_function() { + assert_evals_to!( + indoc!( + r#" + x : List [ Foo Str ] + x = List.map [ "a", "b" ] Foo + + x + "# + ), + RocList::from_slice(&[ + RocStr::from_slice("a".as_bytes()), + RocStr::from_slice("b".as_bytes()) + ]), + RocList + ); +} + +#[test] +fn applied_tag_function_result() { + assert_evals_to!( + indoc!( + r#" + x : List (Result Str *) + x = List.map [ "a", "b" ] Ok + + x + "# + ), + RocList::from_slice(&[ + (1, RocStr::from_slice("a".as_bytes())), + (1, RocStr::from_slice("b".as_bytes())) + ]), + RocList<(i64, RocStr)> + ); +} + +#[test] +fn applied_tag_function_linked_list() { + assert_evals_to!( + indoc!( + r#" + ConsList a : [ Nil, Cons a (ConsList a) ] + + x : List (ConsList Str) + x = List.map2 [ "a", "b" ] [ Nil, Cons "c" Nil ] Cons + + when List.first x is + Ok (Cons "a" Nil) -> 1 + _ -> 0 + "# + ), + 1, + i64 + ); +} From 76289040198d36b6ef34e845283eaa9bc31478e1 Mon Sep 17 00:00:00 2001 From: rvcas Date: Sun, 28 Mar 2021 17:00:50 -0400 Subject: [PATCH 20/32] feat(mono/ir): implement applied tag functions --- compiler/mono/src/ir.rs | 62 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 0a7d3cde2b..bf1aad9bd0 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -2803,18 +2803,66 @@ pub fn with_hole<'a>( variant_var, name: tag_name, arguments: args, - .. + ext_var, } => { use crate::layout::UnionVariant::*; let arena = env.arena; - match layout_cache.from_var(arena, variant_var, env.subs) { - Err(e) => panic!("invalid layout {:?}", e), - Ok(Layout::FunctionPointer(argument_layouts, ret_layout)) => { - dbg!(argument_layouts, ret_layout); + let desc = env.subs.get_without_compacting(variant_var); + + if let Content::Structure(FlatType::Func(arg_vars, _, ret_var)) = desc.content { + let mut loc_pattern_args = vec![]; + let mut loc_expr_args = vec![]; + + let proc_symbol = env.unique_symbol(); + + for arg_var in arg_vars { + let arg_symbol = env.unique_symbol(); + + let loc_pattern = + Located::at_zero(roc_can::pattern::Pattern::Identifier(arg_symbol)); + + let loc_expr = Located::at_zero(roc_can::expr::Expr::Var(arg_symbol)); + + loc_pattern_args.push((arg_var, loc_pattern)); + loc_expr_args.push((arg_var, loc_expr)); } - Ok(_) => { - panic!("idk") + + let loc_body = Located::at_zero(roc_can::expr::Expr::Tag { + variant_var: ret_var, + name: tag_name, + arguments: loc_expr_args, + ext_var, + }); + + let inserted = procs.insert_anonymous( + env, + proc_symbol, + variant_var, + loc_pattern_args, + loc_body, + CapturedSymbols::None, + ret_var, + layout_cache, + ); + + match inserted { + Ok(layout) => { + return Stmt::Let( + assigned, + call_by_pointer(env, procs, proc_symbol, layout), + layout, + hole, + ); + } + Err(runtime_error) => { + return Stmt::RuntimeError(env.arena.alloc(format!( + "RuntimeError {} line {} {:?}", + file!(), + line!(), + runtime_error, + ))); + } } } From a0b9d8c5ebe3a0db2a49968e690a3addfa310682 Mon Sep 17 00:00:00 2001 From: rvcas Date: Sun, 28 Mar 2021 17:06:40 -0400 Subject: [PATCH 21/32] chore(mono/layout): remove useless import --- compiler/mono/src/layout.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/mono/src/layout.rs b/compiler/mono/src/layout.rs index 7cd65a7471..975b16d6f9 100644 --- a/compiler/mono/src/layout.rs +++ b/compiler/mono/src/layout.rs @@ -1,7 +1,6 @@ use crate::ir::Parens; use bumpalo::collections::Vec; use bumpalo::Bump; -use core::panic; use roc_collections::all::{default_hasher, MutMap, MutSet}; use roc_module::ident::{Lowercase, TagName}; use roc_module::symbol::{Interns, Symbol}; From e4babd4445a5258c96d828b051da1dcc4bd94e29 Mon Sep 17 00:00:00 2001 From: rvcas Date: Sun, 28 Mar 2021 23:42:29 -0400 Subject: [PATCH 22/32] chore(unify): move re-used logic into a helper function for TagUnion and Func --- compiler/unify/src/unify.rs | 105 ++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 52 deletions(-) diff --git a/compiler/unify/src/unify.rs b/compiler/unify/src/unify.rs index 258e044a41..39688d5527 100644 --- a/compiler/unify/src/unify.rs +++ b/compiler/unify/src/unify.rs @@ -1072,60 +1072,10 @@ fn unify_flat_type( } } (TagUnion(tags, ext), Func(args, closure, ret)) if tags.len() == 1 => { - let (tag_name, payload) = tags.iter().next().unwrap(); - - if payload.is_empty() { - let mut new_tags = MutMap::with_capacity_and_hasher(1, default_hasher()); - - new_tags.insert(tag_name.clone(), args.clone()); - - let content = Structure(TagUnion(new_tags, *ext)); - - let new_tag_union_var = fresh(subs, pool, ctx, content); - - let problems = unify_pool(subs, pool, new_tag_union_var, *ret); - - if problems.is_empty() { - let desc = subs.get(ctx.second); - subs.union(ctx.first, ctx.second, desc); - } - - problems - } else { - mismatch!( - "Trying to unify two flat types that are incompatible: {:?} ~ {:?}", - TagUnion(tags.clone(), *ext), - Func(args.clone(), *closure, *ret) - ) - } + unify_tag_union_and_func(tags, args, subs, pool, ctx, ext, ret, closure, true) } (Func(args, closure, ret), TagUnion(tags, ext)) if tags.len() == 1 => { - let (tag_name, payload) = tags.iter().next().unwrap(); - - if payload.is_empty() { - let mut new_tags = MutMap::with_capacity_and_hasher(1, default_hasher()); - - new_tags.insert(tag_name.clone(), args.clone()); - - let content = Structure(TagUnion(new_tags, *ext)); - - let new_tag_union_var = fresh(subs, pool, ctx, content); - - let problems = unify_pool(subs, pool, *ret, new_tag_union_var); - - if problems.is_empty() { - let desc = subs.get(ctx.first); - subs.union(ctx.first, ctx.second, desc); - } - - problems - } else { - mismatch!( - "Trying to unify two flat types that are incompatible: {:?} ~ {:?}", - Func(args.clone(), *closure, *ret), - TagUnion(tags.clone(), *ext), - ) - } + unify_tag_union_and_func(tags, args, subs, pool, ctx, ext, ret, closure, false) } (other1, other2) => mismatch!( "Trying to unify two flat types that are incompatible: {:?} ~ {:?}", @@ -1308,3 +1258,54 @@ fn is_recursion_var(subs: &Subs, var: Variable) -> bool { Content::RecursionVar { .. } ) } + +#[allow(clippy::too_many_arguments, clippy::ptr_arg)] +fn unify_tag_union_and_func( + tags: &MutMap>, + args: &Vec, + subs: &mut Subs, + pool: &mut Pool, + ctx: &Context, + ext: &Variable, + ret: &Variable, + closure: &Variable, + left: bool, +) -> Outcome { + use FlatType::*; + + let (tag_name, payload) = tags.iter().next().unwrap(); + + if payload.is_empty() { + let mut new_tags = MutMap::with_capacity_and_hasher(1, default_hasher()); + + new_tags.insert(tag_name.clone(), args.to_owned()); + + let content = Structure(TagUnion(new_tags, *ext)); + + let new_tag_union_var = fresh(subs, pool, ctx, content); + + let problems = if left { + unify_pool(subs, pool, new_tag_union_var, *ret) + } else { + unify_pool(subs, pool, *ret, new_tag_union_var) + }; + + if problems.is_empty() { + let desc = if left { + subs.get(ctx.second) + } else { + subs.get(ctx.first) + }; + + subs.union(ctx.first, ctx.second, desc); + } + + problems + } else { + mismatch!( + "Trying to unify two flat types that are incompatible: {:?} ~ {:?}", + TagUnion(tags.clone(), *ext), + Func(args.to_owned(), *closure, *ret) + ) + } +} From a1f6fb206a30de22426d7867a19728c1ecc4aeb0 Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Mon, 29 Mar 2021 11:31:11 +0200 Subject: [PATCH 23/32] actually set ed_model.dirty=false after new render --- editor/src/editor/main.rs | 2 ++ editor/src/editor/mvc/ed_update.rs | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/editor/src/editor/main.rs b/editor/src/editor/main.rs index 8388e528d1..392825cf56 100644 --- a/editor/src/editor/main.rs +++ b/editor/src/editor/main.rs @@ -302,6 +302,8 @@ fn run_event_loop(file_path_opt: Option<&Path>) -> Result<(), Box> { Ok(rendered_wgpu) => rendered_wgpu_opt = Some(rendered_wgpu), Err(e) => print_err(&e), } + + ed_model.dirty = false; } if let Some(ref rendered_wgpu) = rendered_wgpu_opt { diff --git a/editor/src/editor/mvc/ed_update.rs b/editor/src/editor/mvc/ed_update.rs index f6e02aa353..953906a786 100644 --- a/editor/src/editor/mvc/ed_update.rs +++ b/editor/src/editor/mvc/ed_update.rs @@ -295,21 +295,25 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult // TODO set all selections to none // TODO nested records + ed_model.dirty = true; + match received_char { '{' => { start_new_record(ed_model)?; } ':' => { // TODO set up Dict if previous char is '{' + update_record_colon(ed_model)?; } '\u{8}' | '\u{7f}' => { // On Linux, '\u{8}' is backspace, // On macOS '\u{7f}'. - unimplemented!("TODO implement backspace") + + unimplemented!("TODO implement backspace"); } ch if is_newline(ch) => { - unimplemented!("TODO implement newline") + unimplemented!("TODO implement newline"); } '\u{1}' // Ctrl + A | '\u{3}' // Ctrl + C @@ -320,6 +324,7 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult | '\u{100000}'..='\u{10fffd}' // ^ => { // chars that can be ignored + ed_model.dirty = false; } ch => { let old_caret_pos = ed_model.get_caret(); From 661f5f2adc89eea7dfd30c08d8d8e36bd3a6cf1d Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Mon, 29 Mar 2021 11:52:17 +0200 Subject: [PATCH 24/32] removed unnecessary clone --- editor/src/editor/markup/attribute.rs | 16 ++++++++-------- editor/src/editor/markup/nodes.rs | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/editor/src/editor/markup/attribute.rs b/editor/src/editor/markup/attribute.rs index a84525c1a3..304c4aadce 100644 --- a/editor/src/editor/markup/attribute.rs +++ b/editor/src/editor/markup/attribute.rs @@ -15,36 +15,36 @@ impl Caret { } } } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct SelectionStart { offset_col: usize, } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct SelectionEnd { offset_col: usize, } // Highlight is used for example when searching for a specific string to highlight all search results in the module -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct HighlightStart { offset_col: usize, } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct HighlightEnd { offset_col: usize, } // Underline is used for warnings and errors -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct UnderlineStart { offset_col: usize, } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct UnderlineEnd { offset_col: usize, } -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum Attribute { // Rust does not yet support types for enum variants so we have to do it like this Caret { caret: Caret }, @@ -59,7 +59,7 @@ pub enum Attribute { UnderlineEnd { underline_end: UnderlineEnd }, } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Attributes { pub all: Vec, } diff --git a/editor/src/editor/markup/nodes.rs b/editor/src/editor/markup/nodes.rs index 534fcd69bf..6bcd46bba8 100644 --- a/editor/src/editor/markup/nodes.rs +++ b/editor/src/editor/markup/nodes.rs @@ -13,7 +13,7 @@ use crate::lang::{ }; use bumpalo::Bump; -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum MarkupNode { Nested { ast_node_id: NodeId, From 0cb6af04fd011fb9ba9cc95e88aaac3f37b3204d Mon Sep 17 00:00:00 2001 From: Anton-4 <17049058+Anton-4@users.noreply.github.com> Date: Mon, 29 Mar 2021 12:02:33 +0200 Subject: [PATCH 25/32] expanded comment --- editor/src/editor/mvc/ed_update.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/src/editor/mvc/ed_update.rs b/editor/src/editor/mvc/ed_update.rs index 953906a786..8adfc36d53 100644 --- a/editor/src/editor/mvc/ed_update.rs +++ b/editor/src/editor/mvc/ed_update.rs @@ -41,7 +41,7 @@ impl<'a> EdModel<'a> { Ok(()) } - // TODO delete + // TODO delete once we use `GridNodeMap` for everything that now uses `CodeLines`. // 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_right(&mut self) { From 1302ee296f1f00e34b1c487ad14fe96f56dc7079 Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 29 Mar 2021 16:32:43 +0200 Subject: [PATCH 26/32] refactor list walking --- compiler/builtins/bitcode/src/list.zig | 36 +++++++++++ compiler/builtins/src/std.rs | 28 +++++++++ compiler/can/src/builtins.rs | 57 +++-------------- compiler/gen/src/llvm/build.rs | 77 ++++++++--------------- compiler/gen/src/llvm/build_list.rs | 86 +++++++++++++------------- compiler/module/src/low_level.rs | 1 + compiler/module/src/symbol.rs | 1 + compiler/mono/src/borrow.rs | 5 +- 8 files changed, 144 insertions(+), 147 deletions(-) diff --git a/compiler/builtins/bitcode/src/list.zig b/compiler/builtins/bitcode/src/list.zig index 139163af1d..285482e293 100644 --- a/compiler/builtins/bitcode/src/list.zig +++ b/compiler/builtins/bitcode/src/list.zig @@ -502,6 +502,42 @@ pub fn listWalkBackwards(list: RocList, stepper: Opaque, stepper_caller: Caller2 utils.decref(std.heap.c_allocator, alignment, list.bytes, data_bytes); } +pub fn listWalkUntil(list: RocList, stepper: Opaque, stepper_caller: Caller2, accum: Opaque, alignment: usize, element_width: usize, accum_width: usize, output: Opaque) callconv(.C) void { + if (accum_width == 0) { + return; + } + + if (list.isEmpty()) { + @memcpy(output orelse unreachable, accum orelse unreachable, accum_width); + return; + } + + const alloc: [*]u8 = @ptrCast([*]u8, std.heap.c_allocator.alloc(u8, accum_width) catch unreachable); + var b1 = output orelse unreachable; + var b2 = alloc; + + @memcpy(b2, accum orelse unreachable, accum_width); + + if (list.bytes) |source_ptr| { + var i: usize = 0; + const size = list.len(); + while (i < size) : (i += 1) { + const element = source_ptr + i * element_width; + stepper_caller(stepper, element, b2, b1); + + const temp = b1; + b2 = b1; + b1 = temp; + } + } + + @memcpy(output orelse unreachable, b2, accum_width); + std.heap.c_allocator.free(alloc[0..accum_width]); + + const data_bytes = list.len() * element_width; + utils.decref(std.heap.c_allocator, alignment, list.bytes, data_bytes); +} + // List.contains : List k, k -> Bool pub fn listContains(list: RocList, key: Opaque, key_width: usize, is_eq: EqFn) callconv(.C) bool { if (list.bytes) |source_ptr| { diff --git a/compiler/builtins/src/std.rs b/compiler/builtins/src/std.rs index 4c4b13d99a..a98fcd4dc9 100644 --- a/compiler/builtins/src/std.rs +++ b/compiler/builtins/src/std.rs @@ -771,6 +771,34 @@ pub fn types() -> MutMap { ), ); + fn until_type(content: SolvedType) -> SolvedType { + // [ LT, EQ, GT ] + SolvedType::TagUnion( + vec![ + (TagName::Global("Continue".into()), vec![content.clone()]), + (TagName::Global("Stop".into()), vec![content]), + ], + Box::new(SolvedType::EmptyTagUnion), + ) + } + + // walkUntil : List elem, (elem -> accum -> [ Continue accum, Stop accum ]), accum -> accum + add_type( + Symbol::LIST_WALK_UNTIL, + top_level_function( + vec![ + list_type(flex(TVAR1)), + closure( + vec![flex(TVAR1), flex(TVAR2)], + TVAR3, + Box::new(until_type(flex(TVAR2))), + ), + flex(TVAR2), + ], + Box::new(flex(TVAR2)), + ), + ); + // keepIf : List elem, (elem -> Bool) -> List elem add_type( Symbol::LIST_KEEP_IF, diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index 334fee9cda..50ac3a1100 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -89,6 +89,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option LIST_KEEP_ERRS=> list_keep_errs, LIST_WALK => list_walk, LIST_WALK_BACKWARDS => list_walk_backwards, + LIST_WALK_UNTIL => list_walk_until, DICT_TEST_HASH => dict_hash_test_only, DICT_LEN => dict_len, DICT_EMPTY => dict_empty, @@ -231,6 +232,7 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap { Symbol::LIST_KEEP_ERRS=> list_keep_errs, Symbol::LIST_WALK => list_walk, Symbol::LIST_WALK_BACKWARDS => list_walk_backwards, + Symbol::LIST_WALK_UNTIL => list_walk_until, Symbol::DICT_TEST_HASH => dict_hash_test_only, Symbol::DICT_LEN => dict_len, Symbol::DICT_EMPTY => dict_empty, @@ -2094,60 +2096,17 @@ fn list_join(symbol: Symbol, var_store: &mut VarStore) -> Def { /// List.walk : List elem, (elem -> accum -> accum), accum -> accum fn list_walk(symbol: Symbol, var_store: &mut VarStore) -> Def { - let list_var = var_store.fresh(); - let func_var = var_store.fresh(); - let accum_var = var_store.fresh(); - - let body = RunLowLevel { - op: LowLevel::ListWalk, - args: vec![ - (list_var, Var(Symbol::ARG_1)), - (func_var, Var(Symbol::ARG_2)), - (accum_var, Var(Symbol::ARG_3)), - ], - ret_var: accum_var, - }; - - defn( - symbol, - vec![ - (list_var, Symbol::ARG_1), - (func_var, Symbol::ARG_2), - (accum_var, Symbol::ARG_3), - ], - var_store, - body, - accum_var, - ) + lowlevel_3(symbol, LowLevel::ListWalk, var_store) } /// List.walkBackwards : List elem, (elem -> accum -> accum), accum -> accum fn list_walk_backwards(symbol: Symbol, var_store: &mut VarStore) -> Def { - let list_var = var_store.fresh(); - let func_var = var_store.fresh(); - let accum_var = var_store.fresh(); + lowlevel_3(symbol, LowLevel::ListWalkBackwards, var_store) +} - let body = RunLowLevel { - op: LowLevel::ListWalkBackwards, - args: vec![ - (list_var, Var(Symbol::ARG_1)), - (func_var, Var(Symbol::ARG_2)), - (accum_var, Var(Symbol::ARG_3)), - ], - ret_var: accum_var, - }; - - defn( - symbol, - vec![ - (list_var, Symbol::ARG_1), - (func_var, Symbol::ARG_2), - (accum_var, Symbol::ARG_3), - ], - var_store, - body, - accum_var, - ) +/// List.walkUntil : List elem, (elem, accum -> [ Continue accum, Stop accum ]), accum -> accum +fn list_walk_until(symbol: Symbol, var_store: &mut VarStore) -> Def { + lowlevel_3(symbol, LowLevel::ListWalkUntil, var_store) } /// List.sum : List (Num a) -> Num a diff --git a/compiler/gen/src/llvm/build.rs b/compiler/gen/src/llvm/build.rs index 4dfda5e9fa..65e7ddb604 100644 --- a/compiler/gen/src/llvm/build.rs +++ b/compiler/gen/src/llvm/build.rs @@ -8,7 +8,7 @@ use crate::llvm::build_list::{ allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat, list_contains, list_get_unsafe, list_join, list_keep_errs, list_keep_if, list_keep_oks, list_len, list_map, list_map2, list_map3, list_map_with_index, list_prepend, list_product, list_repeat, - list_reverse, list_set, list_single, list_sum, list_walk, list_walk_backwards, + list_reverse, list_set, list_single, list_sum, list_walk_help, }; use crate::llvm::build_str::{ str_concat, str_count_graphemes, str_ends_with, str_from_float, str_from_int, str_from_utf8, @@ -3879,57 +3879,30 @@ fn run_low_level<'a, 'ctx, 'env>( list_contains(env, layout_ids, elem, elem_layout, list) } - ListWalk => { - debug_assert_eq!(args.len(), 3); - - let (list, list_layout) = load_symbol_and_layout(scope, &args[0]); - - let (func, func_layout) = load_symbol_and_layout(scope, &args[1]); - - let (default, default_layout) = load_symbol_and_layout(scope, &args[2]); - - match list_layout { - Layout::Builtin(Builtin::EmptyList) => default, - Layout::Builtin(Builtin::List(_, element_layout)) => list_walk( - env, - layout_ids, - parent, - list, - element_layout, - func, - func_layout, - default, - default_layout, - ), - _ => unreachable!("invalid list layout"), - } - } - ListWalkBackwards => { - // List.walkBackwards : List elem, (elem -> accum -> accum), accum -> accum - debug_assert_eq!(args.len(), 3); - - let (list, list_layout) = load_symbol_and_layout(scope, &args[0]); - - let (func, func_layout) = load_symbol_and_layout(scope, &args[1]); - - let (default, default_layout) = load_symbol_and_layout(scope, &args[2]); - - match list_layout { - Layout::Builtin(Builtin::EmptyList) => default, - Layout::Builtin(Builtin::List(_, element_layout)) => list_walk_backwards( - env, - layout_ids, - parent, - list, - element_layout, - func, - func_layout, - default, - default_layout, - ), - _ => unreachable!("invalid list layout"), - } - } + ListWalk => list_walk_help( + env, + layout_ids, + scope, + parent, + args, + crate::llvm::build_list::ListWalk::Walk, + ), + ListWalkUntil => list_walk_help( + env, + layout_ids, + scope, + parent, + args, + crate::llvm::build_list::ListWalk::WalkUntil, + ), + ListWalkBackwards => list_walk_help( + env, + layout_ids, + scope, + parent, + args, + crate::llvm::build_list::ListWalk::WalkBackwards, + ), ListSum => { debug_assert_eq!(args.len(), 1); diff --git a/compiler/gen/src/llvm/build_list.rs b/compiler/gen/src/llvm/build_list.rs index 40321129db..7ef207e816 100644 --- a/compiler/gen/src/llvm/build_list.rs +++ b/compiler/gen/src/llvm/build_list.rs @@ -863,56 +863,54 @@ pub fn list_product<'a, 'ctx, 'env>( builder.build_load(accum_alloca, "load_final_acum") } -/// List.walk : List elem, (elem -> accum -> accum), accum -> accum -pub fn list_walk<'a, 'ctx, 'env>( - env: &Env<'a, 'ctx, 'env>, - layout_ids: &mut LayoutIds<'a>, - parent: FunctionValue<'ctx>, - list: BasicValueEnum<'ctx>, - element_layout: &Layout<'a>, - func: BasicValueEnum<'ctx>, - func_layout: &Layout<'a>, - default: BasicValueEnum<'ctx>, - default_layout: &Layout<'a>, -) -> BasicValueEnum<'ctx> { - list_walk_generic( - env, - layout_ids, - parent, - list, - element_layout, - func, - func_layout, - default, - default_layout, - &bitcode::LIST_WALK, - ) +pub enum ListWalk { + Walk, + WalkBackwards, + WalkUntil, + WalkBackwardsUntil, } -/// List.walkBackwards : List elem, (elem -> accum -> accum), accum -> accum -pub fn list_walk_backwards<'a, 'ctx, 'env>( +pub fn list_walk_help<'a, 'ctx, 'env>( env: &Env<'a, 'ctx, 'env>, layout_ids: &mut LayoutIds<'a>, + scope: &crate::llvm::build::Scope<'a, 'ctx>, parent: FunctionValue<'ctx>, - list: BasicValueEnum<'ctx>, - element_layout: &Layout<'a>, - func: BasicValueEnum<'ctx>, - func_layout: &Layout<'a>, - default: BasicValueEnum<'ctx>, - default_layout: &Layout<'a>, + args: &[roc_module::symbol::Symbol], + variant: ListWalk, ) -> BasicValueEnum<'ctx> { - list_walk_generic( - env, - layout_ids, - parent, - list, - element_layout, - func, - func_layout, - default, - default_layout, - &bitcode::LIST_WALK_BACKWARDS, - ) + use crate::llvm::build::load_symbol_and_layout; + + debug_assert_eq!(args.len(), 3); + + let (list, list_layout) = load_symbol_and_layout(scope, &args[0]); + + let (func, func_layout) = load_symbol_and_layout(scope, &args[1]); + + let (default, default_layout) = load_symbol_and_layout(scope, &args[2]); + + let bitcode_fn = match variant { + ListWalk::Walk => bitcode::LIST_WALK, + ListWalk::WalkBackwards => bitcode::LIST_WALK_BACKWARDS, + ListWalk::WalkUntil => todo!(), + ListWalk::WalkBackwardsUntil => todo!(), + }; + + match list_layout { + Layout::Builtin(Builtin::EmptyList) => default, + Layout::Builtin(Builtin::List(_, element_layout)) => list_walk_generic( + env, + layout_ids, + parent, + list, + element_layout, + func, + func_layout, + default, + default_layout, + &bitcode_fn, + ), + _ => unreachable!("invalid list layout"), + } } fn list_walk_generic<'a, 'ctx, 'env>( diff --git a/compiler/module/src/low_level.rs b/compiler/module/src/low_level.rs index 3922bb8e6d..24f9f815e2 100644 --- a/compiler/module/src/low_level.rs +++ b/compiler/module/src/low_level.rs @@ -32,6 +32,7 @@ pub enum LowLevel { ListMapWithIndex, ListKeepIf, ListWalk, + ListWalkUntil, ListWalkBackwards, ListSum, ListProduct, diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index 85806a99c5..1332e0487f 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -915,6 +915,7 @@ define_builtins! { 24 LIST_MAP2: "map2" 25 LIST_MAP3: "map3" 26 LIST_PRODUCT: "product" + 27 LIST_WALK_UNTIL: "walkUntil" } 5 RESULT: "Result" => { 0 RESULT_RESULT: "Result" imported // the Result.Result type alias diff --git a/compiler/mono/src/borrow.rs b/compiler/mono/src/borrow.rs index f48c787194..6545158512 100644 --- a/compiler/mono/src/borrow.rs +++ b/compiler/mono/src/borrow.rs @@ -655,8 +655,9 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] { ListMap3 => arena.alloc_slice_copy(&[owned, owned, owned, irrelevant]), ListKeepIf | ListKeepOks | ListKeepErrs => arena.alloc_slice_copy(&[owned, borrowed]), ListContains => arena.alloc_slice_copy(&[borrowed, irrelevant]), - ListWalk => arena.alloc_slice_copy(&[owned, irrelevant, owned]), - ListWalkBackwards => arena.alloc_slice_copy(&[owned, irrelevant, owned]), + ListWalk | ListWalkUntil | ListWalkBackwards => { + arena.alloc_slice_copy(&[owned, irrelevant, owned]) + } ListSum | ListProduct => arena.alloc_slice_copy(&[borrowed]), // TODO when we have lists with capacity (if ever) From 1dd8c25e12e99d3e85640d85f940fbc9517ef845 Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 29 Mar 2021 20:54:38 +0200 Subject: [PATCH 27/32] implement walk until --- compiler/builtins/bitcode/src/list.zig | 31 +++++++---- compiler/builtins/bitcode/src/main.zig | 1 + compiler/builtins/src/bitcode.rs | 1 + compiler/gen/src/llvm/build_list.rs | 71 +++++++++++++++++--------- compiler/test_gen/src/gen_list.rs | 26 ++++++++++ 5 files changed, 95 insertions(+), 35 deletions(-) diff --git a/compiler/builtins/bitcode/src/list.zig b/compiler/builtins/bitcode/src/list.zig index 285482e293..14ad92d698 100644 --- a/compiler/builtins/bitcode/src/list.zig +++ b/compiler/builtins/bitcode/src/list.zig @@ -4,6 +4,8 @@ const RocResult = utils.RocResult; const mem = std.mem; const Allocator = mem.Allocator; +const TAG_WIDTH = 8; + const EqFn = fn (?[*]u8, ?[*]u8) callconv(.C) bool; const Opaque = ?[*]u8; @@ -502,7 +504,10 @@ pub fn listWalkBackwards(list: RocList, stepper: Opaque, stepper_caller: Caller2 utils.decref(std.heap.c_allocator, alignment, list.bytes, data_bytes); } -pub fn listWalkUntil(list: RocList, stepper: Opaque, stepper_caller: Caller2, accum: Opaque, alignment: usize, element_width: usize, accum_width: usize, output: Opaque) callconv(.C) void { +pub fn listWalkUntil(list: RocList, stepper: Opaque, stepper_caller: Caller2, accum: Opaque, alignment: usize, element_width: usize, accum_width: usize, dec: Dec, output: Opaque) callconv(.C) void { + // [ Continue a, Stop a ] + const CONTINUE: usize = 0; + if (accum_width == 0) { return; } @@ -512,27 +517,31 @@ pub fn listWalkUntil(list: RocList, stepper: Opaque, stepper_caller: Caller2, ac return; } - const alloc: [*]u8 = @ptrCast([*]u8, std.heap.c_allocator.alloc(u8, accum_width) catch unreachable); - var b1 = output orelse unreachable; - var b2 = alloc; + const alloc: [*]u8 = @ptrCast([*]u8, std.heap.c_allocator.alloc(u8, TAG_WIDTH + accum_width) catch unreachable); - @memcpy(b2, accum orelse unreachable, accum_width); + @memcpy(alloc + TAG_WIDTH, accum orelse unreachable, accum_width); if (list.bytes) |source_ptr| { var i: usize = 0; const size = list.len(); while (i < size) : (i += 1) { const element = source_ptr + i * element_width; - stepper_caller(stepper, element, b2, b1); + stepper_caller(stepper, element, alloc + TAG_WIDTH, alloc); - const temp = b1; - b2 = b1; - b1 = temp; + const usizes: [*]usize = @ptrCast([*]usize, @alignCast(8, alloc)); + if (usizes[0] != 0) { + // decrement refcount of the remaining items + i += 1; + while (i < size) : (i += 1) { + dec(source_ptr + i * element_width); + } + break; + } } } - @memcpy(output orelse unreachable, b2, accum_width); - std.heap.c_allocator.free(alloc[0..accum_width]); + @memcpy(output orelse unreachable, alloc + TAG_WIDTH, accum_width); + std.heap.c_allocator.free(alloc[0 .. TAG_WIDTH + accum_width]); const data_bytes = list.len() * element_width; utils.decref(std.heap.c_allocator, alignment, list.bytes, data_bytes); diff --git a/compiler/builtins/bitcode/src/main.zig b/compiler/builtins/bitcode/src/main.zig index 4cd57a84a5..be9d3bb832 100644 --- a/compiler/builtins/bitcode/src/main.zig +++ b/compiler/builtins/bitcode/src/main.zig @@ -12,6 +12,7 @@ comptime { exportListFn(list.listMapWithIndex, "map_with_index"); exportListFn(list.listKeepIf, "keep_if"); exportListFn(list.listWalk, "walk"); + exportListFn(list.listWalkUntil, "walkUntil"); exportListFn(list.listWalkBackwards, "walk_backwards"); exportListFn(list.listKeepOks, "keep_oks"); exportListFn(list.listKeepErrs, "keep_errs"); diff --git a/compiler/builtins/src/bitcode.rs b/compiler/builtins/src/bitcode.rs index 0fd6cb1f2f..eeef60f61e 100644 --- a/compiler/builtins/src/bitcode.rs +++ b/compiler/builtins/src/bitcode.rs @@ -70,6 +70,7 @@ pub const LIST_KEEP_IF: &str = "roc_builtins.list.keep_if"; pub const LIST_KEEP_OKS: &str = "roc_builtins.list.keep_oks"; pub const LIST_KEEP_ERRS: &str = "roc_builtins.list.keep_errs"; pub const LIST_WALK: &str = "roc_builtins.list.walk"; +pub const LIST_WALK_UNTIL: &str = "roc_builtins.list.walkUntil"; pub const LIST_WALK_BACKWARDS: &str = "roc_builtins.list.walk_backwards"; pub const LIST_CONTAINS: &str = "roc_builtins.list.contains"; pub const LIST_REPEAT: &str = "roc_builtins.list.repeat"; diff --git a/compiler/gen/src/llvm/build_list.rs b/compiler/gen/src/llvm/build_list.rs index 7ef207e816..b799e62ac1 100644 --- a/compiler/gen/src/llvm/build_list.rs +++ b/compiler/gen/src/llvm/build_list.rs @@ -888,13 +888,6 @@ pub fn list_walk_help<'a, 'ctx, 'env>( let (default, default_layout) = load_symbol_and_layout(scope, &args[2]); - let bitcode_fn = match variant { - ListWalk::Walk => bitcode::LIST_WALK, - ListWalk::WalkBackwards => bitcode::LIST_WALK_BACKWARDS, - ListWalk::WalkUntil => todo!(), - ListWalk::WalkBackwardsUntil => todo!(), - }; - match list_layout { Layout::Builtin(Builtin::EmptyList) => default, Layout::Builtin(Builtin::List(_, element_layout)) => list_walk_generic( @@ -907,7 +900,7 @@ pub fn list_walk_help<'a, 'ctx, 'env>( func_layout, default, default_layout, - &bitcode_fn, + variant, ), _ => unreachable!("invalid list layout"), } @@ -923,10 +916,17 @@ fn list_walk_generic<'a, 'ctx, 'env>( func_layout: &Layout<'a>, default: BasicValueEnum<'ctx>, default_layout: &Layout<'a>, - zig_function: &str, + variant: ListWalk, ) -> BasicValueEnum<'ctx> { let builder = env.builder; + let zig_function = match variant { + ListWalk::Walk => bitcode::LIST_WALK, + ListWalk::WalkBackwards => bitcode::LIST_WALK_BACKWARDS, + ListWalk::WalkUntil => bitcode::LIST_WALK_UNTIL, + ListWalk::WalkBackwardsUntil => todo!(), + }; + let u8_ptr = env.context.i8_type().ptr_type(AddressSpace::Generic); let list_i128 = complex_bitcast(env.builder, list, env.context.i128_type().into(), "to_i128"); @@ -959,21 +959,44 @@ fn list_walk_generic<'a, 'ctx, 'env>( let result_ptr = env.builder.build_alloca(default.get_type(), "result"); - call_void_bitcode_fn( - env, - &[ - list_i128, - env.builder - .build_bitcast(transform_ptr, u8_ptr, "to_opaque"), - stepper_caller.into(), - env.builder.build_bitcast(default_ptr, u8_ptr, "to_u8_ptr"), - alignment_iv.into(), - element_width.into(), - default_width.into(), - env.builder.build_bitcast(result_ptr, u8_ptr, "to_opaque"), - ], - zig_function, - ); + match variant { + ListWalk::Walk | ListWalk::WalkBackwards => { + call_void_bitcode_fn( + env, + &[ + list_i128, + env.builder + .build_bitcast(transform_ptr, u8_ptr, "to_opaque"), + stepper_caller.into(), + env.builder.build_bitcast(default_ptr, u8_ptr, "to_u8_ptr"), + alignment_iv.into(), + element_width.into(), + default_width.into(), + env.builder.build_bitcast(result_ptr, u8_ptr, "to_opaque"), + ], + zig_function, + ); + } + ListWalk::WalkUntil | ListWalk::WalkBackwardsUntil => { + let dec_element_fn = build_dec_wrapper(env, layout_ids, element_layout); + call_void_bitcode_fn( + env, + &[ + list_i128, + env.builder + .build_bitcast(transform_ptr, u8_ptr, "to_opaque"), + stepper_caller.into(), + env.builder.build_bitcast(default_ptr, u8_ptr, "to_u8_ptr"), + alignment_iv.into(), + element_width.into(), + default_width.into(), + dec_element_fn.as_global_value().as_pointer_value().into(), + env.builder.build_bitcast(result_ptr, u8_ptr, "to_opaque"), + ], + zig_function, + ); + } + } env.builder.build_load(result_ptr, "load_result") } diff --git a/compiler/test_gen/src/gen_list.rs b/compiler/test_gen/src/gen_list.rs index 5c256299ba..7c1efa8dcf 100644 --- a/compiler/test_gen/src/gen_list.rs +++ b/compiler/test_gen/src/gen_list.rs @@ -320,6 +320,32 @@ fn list_walk_substraction() { assert_evals_to!(r#"List.walk [ 1, 2 ] Num.sub 1"#, 2, i64); } +#[test] +fn list_walk_until_sum() { + assert_evals_to!( + r#"List.walkUntil [ 1, 2 ] (\a,b -> Continue (a + b)) 0"#, + 3, + i64 + ); +} + +#[test] +fn list_walk_until_even_prefix_sum() { + assert_evals_to!( + r#" + helper = \a, b -> + if Num.isEven a then + Continue (a + b) + + else + Stop b + + List.walkUntil [ 2, 4, 8, 9 ] helper 0"#, + 2 + 4 + 8, + i64 + ); +} + #[test] fn list_keep_if_empty_list_of_int() { assert_evals_to!( From 1df89ccbbe989d084c50c8d859e5f9c6fd6cb18e Mon Sep 17 00:00:00 2001 From: Chadtech Date: Tue, 30 Mar 2021 00:52:47 -0400 Subject: [PATCH 28/32] Aliases now rendered in docs --- compiler/load/src/docs.rs | 12 ++++++++---- docs/tests/fixtures/Interface.roc | 3 +++ docs/tests/test_docs.rs | 4 ++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/compiler/load/src/docs.rs b/compiler/load/src/docs.rs index f5c8bfc6ad..d489bccc7e 100644 --- a/compiler/load/src/docs.rs +++ b/compiler/load/src/docs.rs @@ -111,12 +111,16 @@ fn generate_module_doc<'a>( }, Alias { - name: _, + name, vars: _, ann: _, - } => - // TODO - { + } => { + let entry = DocEntry { + name: name.value.to_string(), + docs: before_comments_or_new_lines.and_then(comments_or_new_lines_to_docs), + }; + acc.push(entry); + (acc, None) } diff --git a/docs/tests/fixtures/Interface.roc b/docs/tests/fixtures/Interface.roc index f03e820518..f9860af81c 100644 --- a/docs/tests/fixtures/Interface.roc +++ b/docs/tests/fixtures/Interface.roc @@ -2,6 +2,9 @@ interface Test exposes [ singleline, multiline, multiparagraph, codeblock ] imports [] +## This is a block +Block : [ @Block ] + ## Single line documentation. singleline : Bool -> Bool diff --git a/docs/tests/test_docs.rs b/docs/tests/test_docs.rs index 1099d88b69..87ba98fd1a 100644 --- a/docs/tests/test_docs.rs +++ b/docs/tests/test_docs.rs @@ -20,6 +20,10 @@ mod test_docs { }; let expected_entries = vec![ + ModuleEntry { + name: "Block".to_string(), + docs: "

This is a block

\n".to_string(), + }, ModuleEntry { name: "singleline".to_string(), docs: "

Single line documentation.

\n".to_string(), From 62b4ce0598f7f0b1105e54644b7b85bebb39ba29 Mon Sep 17 00:00:00 2001 From: Chadtech Date: Tue, 30 Mar 2021 00:53:20 -0400 Subject: [PATCH 29/32] Put List private tag list --- compiler/builtins/docs/List.roc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/builtins/docs/List.roc b/compiler/builtins/docs/List.roc index fbc7a192d7..15ebd4ac92 100644 --- a/compiler/builtins/docs/List.roc +++ b/compiler/builtins/docs/List.roc @@ -195,7 +195,7 @@ interface List2 ## * Even when copying is faster, other list operations may still be slightly slower with persistent data structures. For example, even if it were a persistent data structure, #List.map, #List.fold, and #List.keepIf would all need to traverse every element in the list and build up the result from scratch. These operations are all ## * Roc's compiler optimizes many list operations into in-place mutations behind the scenes, depending on how the list is being used. For example, #List.map, #List.keepIf, and #List.set can all be optimized to perform in-place mutations. ## * If possible, it is usually best for performance to use large lists in a way where the optimizer can turn them into in-place mutations. If this is not possible, a persistent data structure might be faster - but this is a rare enough scenario that it would not be good for the average Roc program's performance if this were the way #List worked by default. Instead, you can look outside Roc's standard modules for an implementation of a persistent data structure - likely built using #List under the hood! -List elem : @List elem +List elem : [ @List elem ] ## Initialize From ce6bdfc75700b0a3c416d513de0dd6b67ed1c001 Mon Sep 17 00:00:00 2001 From: Chadtech Date: Tue, 30 Mar 2021 00:54:04 -0400 Subject: [PATCH 30/32] If the docs encounter a break in the comments, we should forget about the accumulated docs until then, since they did not pertain to what we were building docs for --- compiler/load/src/docs.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/load/src/docs.rs b/compiler/load/src/docs.rs index d489bccc7e..5f8bf24c01 100644 --- a/compiler/load/src/docs.rs +++ b/compiler/load/src/docs.rs @@ -143,7 +143,9 @@ fn comments_or_new_lines_to_docs<'a>( docs.push_str(doc_str); docs.push('\n'); } - Newline | LineComment(_) => {} + Newline | LineComment(_) => { + docs = String::new(); + } } } if docs.is_empty() { From 4be3f04abad4bb518ab518faa4ab468d1a8ea53d Mon Sep 17 00:00:00 2001 From: Chadtech Date: Tue, 30 Mar 2021 00:54:17 -0400 Subject: [PATCH 31/32] Pretty assertions in test_docs --- docs/tests/test_docs.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/tests/test_docs.rs b/docs/tests/test_docs.rs index 87ba98fd1a..02e130f25d 100644 --- a/docs/tests/test_docs.rs +++ b/docs/tests/test_docs.rs @@ -1,3 +1,6 @@ +#[macro_use] +extern crate pretty_assertions; + use roc_docs::{documentation_to_template_data, files_to_documentations, ModuleEntry}; use std::path::PathBuf; From b100efa51106d030ce5599d60a3f1badb5d8c071 Mon Sep 17 00:00:00 2001 From: Chadtech Date: Tue, 30 Mar 2021 01:30:47 -0400 Subject: [PATCH 32/32] Set type in Set.roc --- compiler/builtins/docs/Set.roc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/builtins/docs/Set.roc b/compiler/builtins/docs/Set.roc index d68a5ed85d..bc5211b407 100644 --- a/compiler/builtins/docs/Set.roc +++ b/compiler/builtins/docs/Set.roc @@ -1,7 +1,11 @@ -interface Set2 - exposes [ empty, isEmpty, len, add, drop, map ] +interface Set + exposes [ Set, empty, isEmpty, len, add, drop, map ] imports [] +## Set + +## A Set is an unordered collection of unique elements. +Set elem : [ @Set elem ] ## An empty set. empty : Set *