Merge branch 'trunk' into parser

This commit is contained in:
Richard Feldman 2021-04-29 21:50:04 -04:00 committed by GitHub
commit bae11e4da5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 1533 additions and 808 deletions

View file

@ -30,6 +30,26 @@ pub type SendSet<K> = im::hashset::HashSet<K, BuildHasher>;
pub type BumpMap<'a, K, V> = hashbrown::HashMap<K, V, BuildHasher, hashbrown::BumpWrapper<'a>>; pub type BumpMap<'a, K, V> = hashbrown::HashMap<K, V, BuildHasher, hashbrown::BumpWrapper<'a>>;
pub trait BumpMapDefault<'a> {
fn new_in(arena: &'a bumpalo::Bump) -> Self;
fn with_capacity_in(capacity: usize, arena: &'a bumpalo::Bump) -> Self;
}
impl<'a, K, V> BumpMapDefault<'a> for BumpMap<'a, K, V> {
fn new_in(arena: &'a bumpalo::Bump) -> Self {
hashbrown::HashMap::with_hasher_in(default_hasher(), hashbrown::BumpWrapper(arena))
}
fn with_capacity_in(capacity: usize, arena: &'a bumpalo::Bump) -> Self {
hashbrown::HashMap::with_capacity_and_hasher_in(
capacity,
default_hasher(),
hashbrown::BumpWrapper(arena),
)
}
}
pub fn arena_join<'a, I>(arena: &'a Bump, strings: &mut I, join_str: &str) -> String<'a> pub fn arena_join<'a, I>(arena: &'a Bump, strings: &mut I, join_str: &str) -> String<'a>
where where
I: Iterator<Item = &'a str>, I: Iterator<Item = &'a str>,

View file

@ -842,7 +842,7 @@ struct State<'a> {
/// pending specializations in the same thread. /// pending specializations in the same thread.
pub needs_specialization: MutSet<ModuleId>, pub needs_specialization: MutSet<ModuleId>,
pub all_pending_specializations: MutMap<Symbol, MutMap<Layout<'a>, PendingSpecialization>>, pub all_pending_specializations: MutMap<Symbol, MutMap<Layout<'a>, PendingSpecialization<'a>>>,
pub specializations_in_flight: u32, pub specializations_in_flight: u32,
@ -3969,6 +3969,7 @@ fn add_def_to_module<'a>(
procs.insert_exposed( procs.insert_exposed(
symbol, symbol,
layout, layout,
mono_env.arena,
mono_env.subs, mono_env.subs,
def.annotation, def.annotation,
annotation, annotation,
@ -4021,6 +4022,7 @@ fn add_def_to_module<'a>(
procs.insert_exposed( procs.insert_exposed(
symbol, symbol,
layout, layout,
mono_env.arena,
mono_env.subs, mono_env.subs,
def.annotation, def.annotation,
annotation, annotation,

View file

@ -6,7 +6,7 @@ use crate::layout::{
}; };
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
use bumpalo::Bump; use bumpalo::Bump;
use roc_collections::all::{default_hasher, BumpMap, MutMap, MutSet}; use roc_collections::all::{default_hasher, BumpMap, BumpMapDefault, MutMap, MutSet};
use roc_module::ident::{ForeignSymbol, Lowercase, TagName}; use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
use roc_module::low_level::LowLevel; use roc_module::low_level::LowLevel;
use roc_module::symbol::{IdentIds, ModuleId, Symbol}; use roc_module::symbol::{IdentIds, ModuleId, Symbol};
@ -77,31 +77,35 @@ impl<'a> CapturedSymbols<'a> {
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct PendingSpecialization { pub struct PendingSpecialization<'a> {
solved_type: SolvedType, solved_type: SolvedType,
host_exposed_aliases: MutMap<Symbol, SolvedType>, host_exposed_aliases: BumpMap<'a, Symbol, SolvedType>,
} }
impl PendingSpecialization { impl<'a> PendingSpecialization<'a> {
pub fn from_var(subs: &Subs, var: Variable) -> Self { pub fn from_var(arena: &'a Bump, subs: &Subs, var: Variable) -> Self {
let solved_type = SolvedType::from_var(subs, var); let solved_type = SolvedType::from_var(subs, var);
PendingSpecialization { PendingSpecialization {
solved_type, solved_type,
host_exposed_aliases: MutMap::default(), host_exposed_aliases: BumpMap::new_in(arena),
} }
} }
pub fn from_var_host_exposed( pub fn from_var_host_exposed(
arena: &'a Bump,
subs: &Subs, subs: &Subs,
var: Variable, var: Variable,
host_exposed_aliases: &MutMap<Symbol, Variable>, exposed: &MutMap<Symbol, Variable>,
) -> Self { ) -> Self {
let solved_type = SolvedType::from_var(subs, var); let solved_type = SolvedType::from_var(subs, var);
let host_exposed_aliases = host_exposed_aliases let mut host_exposed_aliases = BumpMap::with_capacity_in(exposed.len(), arena);
.iter()
.map(|(symbol, variable)| (*symbol, SolvedType::from_var(subs, *variable))) host_exposed_aliases.extend(
.collect(); exposed
.iter()
.map(|(symbol, variable)| (*symbol, SolvedType::from_var(subs, *variable))),
);
PendingSpecialization { PendingSpecialization {
solved_type, solved_type,
@ -275,7 +279,8 @@ pub struct Procs<'a> {
pub partial_procs: MutMap<Symbol, PartialProc<'a>>, pub partial_procs: MutMap<Symbol, PartialProc<'a>>,
pub imported_module_thunks: MutSet<Symbol>, pub imported_module_thunks: MutSet<Symbol>,
pub module_thunks: MutSet<Symbol>, pub module_thunks: MutSet<Symbol>,
pub pending_specializations: Option<MutMap<Symbol, MutMap<Layout<'a>, PendingSpecialization>>>, pub pending_specializations:
Option<MutMap<Symbol, MutMap<Layout<'a>, PendingSpecialization<'a>>>>,
pub specialized: MutMap<(Symbol, Layout<'a>), InProgressProc<'a>>, pub specialized: MutMap<(Symbol, Layout<'a>), InProgressProc<'a>>,
pub call_by_pointer_wrappers: MutMap<Symbol, Symbol>, pub call_by_pointer_wrappers: MutMap<Symbol, Symbol>,
pub runtime_errors: MutMap<Symbol, &'a str>, pub runtime_errors: MutMap<Symbol, &'a str>,
@ -502,7 +507,7 @@ impl<'a> Procs<'a> {
// Changing it to use .entry() would necessarily make it incorrect. // Changing it to use .entry() would necessarily make it incorrect.
#[allow(clippy::map_entry)] #[allow(clippy::map_entry)]
if !already_specialized { if !already_specialized {
let pending = PendingSpecialization::from_var(env.subs, annotation); let pending = PendingSpecialization::from_var(env.arena, env.subs, annotation);
let partial_proc; let partial_proc;
if let Some(existing) = self.partial_procs.get(&symbol) { if let Some(existing) = self.partial_procs.get(&symbol) {
@ -576,6 +581,7 @@ impl<'a> Procs<'a> {
&mut self, &mut self,
name: Symbol, name: Symbol,
layout: Layout<'a>, layout: Layout<'a>,
arena: &'a Bump,
subs: &Subs, subs: &Subs,
opt_annotation: Option<roc_can::def::Annotation>, opt_annotation: Option<roc_can::def::Annotation>,
fn_var: Variable, fn_var: Variable,
@ -590,8 +596,9 @@ impl<'a> Procs<'a> {
// We're done with that tuple, so move layout back out to avoid cloning it. // We're done with that tuple, so move layout back out to avoid cloning it.
let (name, layout) = tuple; let (name, layout) = tuple;
let pending = match opt_annotation { let pending = match opt_annotation {
None => PendingSpecialization::from_var(subs, fn_var), None => PendingSpecialization::from_var(arena, subs, fn_var),
Some(annotation) => PendingSpecialization::from_var_host_exposed( Some(annotation) => PendingSpecialization::from_var_host_exposed(
arena,
subs, subs,
fn_var, fn_var,
&annotation.introduced_variables.host_exposed_aliases, &annotation.introduced_variables.host_exposed_aliases,
@ -634,7 +641,7 @@ impl<'a> Procs<'a> {
// We're done with that tuple, so move layout back out to avoid cloning it. // We're done with that tuple, so move layout back out to avoid cloning it.
let (name, layout) = tuple; let (name, layout) = tuple;
let pending = PendingSpecialization::from_var(env.subs, fn_var); let pending = PendingSpecialization::from_var(env.arena, env.subs, fn_var);
// This should only be called when pending_specializations is Some. // This should only be called when pending_specializations is Some.
// Otherwise, it's being called in the wrong pass! // Otherwise, it's being called in the wrong pass!
@ -674,10 +681,10 @@ impl<'a> Procs<'a> {
} }
fn add_pending<'a>( fn add_pending<'a>(
pending_specializations: &mut MutMap<Symbol, MutMap<Layout<'a>, PendingSpecialization>>, pending_specializations: &mut MutMap<Symbol, MutMap<Layout<'a>, PendingSpecialization<'a>>>,
symbol: Symbol, symbol: Symbol,
layout: Layout<'a>, layout: Layout<'a>,
pending: PendingSpecialization, pending: PendingSpecialization<'a>,
) { ) {
let all_pending = pending_specializations let all_pending = pending_specializations
.entry(symbol) .entry(symbol)
@ -1662,7 +1669,7 @@ pub fn specialize_all<'a>(
name, name,
layout_cache, layout_cache,
solved_type, solved_type,
MutMap::default(), BumpMap::new_in(env.arena),
partial_proc, partial_proc,
) { ) {
Ok((proc, layout)) => { Ok((proc, layout)) => {
@ -1833,8 +1840,7 @@ fn specialize_external<'a>(
let host_exposed_layouts = if host_exposed_variables.is_empty() { let host_exposed_layouts = if host_exposed_variables.is_empty() {
HostExposedLayouts::NotHostExposed HostExposedLayouts::NotHostExposed
} else { } else {
let mut aliases = let mut aliases = BumpMap::new_in(env.arena);
hashbrown::HashMap::with_hasher_in(default_hasher(), hashbrown::BumpWrapper(env.arena));
for (symbol, variable) in host_exposed_variables { for (symbol, variable) in host_exposed_variables {
let layout = layout_cache let layout = layout_cache
@ -2336,7 +2342,7 @@ fn specialize_solved_type<'a>(
proc_name: Symbol, proc_name: Symbol,
layout_cache: &mut LayoutCache<'a>, layout_cache: &mut LayoutCache<'a>,
solved_type: SolvedType, solved_type: SolvedType,
host_exposed_aliases: MutMap<Symbol, SolvedType>, host_exposed_aliases: BumpMap<Symbol, SolvedType>,
partial_proc: PartialProc<'a>, partial_proc: PartialProc<'a>,
) -> Result<(Proc<'a>, Layout<'a>), SpecializeFailure<'a>> { ) -> Result<(Proc<'a>, Layout<'a>), SpecializeFailure<'a>> {
// add the specializations that other modules require of us // add the specializations that other modules require of us
@ -4832,7 +4838,7 @@ fn from_can_when<'a>(
) )
} }
fn substitute(substitutions: &MutMap<Symbol, Symbol>, s: Symbol) -> Option<Symbol> { fn substitute(substitutions: &BumpMap<Symbol, Symbol>, s: Symbol) -> Option<Symbol> {
match substitutions.get(&s) { match substitutions.get(&s) {
Some(new) => { Some(new) => {
debug_assert!(!substitutions.contains_key(new)); debug_assert!(!substitutions.contains_key(new));
@ -4843,7 +4849,7 @@ fn substitute(substitutions: &MutMap<Symbol, Symbol>, s: Symbol) -> Option<Symbo
} }
fn substitute_in_exprs<'a>(arena: &'a Bump, stmt: &mut Stmt<'a>, from: Symbol, to: Symbol) { fn substitute_in_exprs<'a>(arena: &'a Bump, stmt: &mut Stmt<'a>, from: Symbol, to: Symbol) {
let mut subs = MutMap::default(); let mut subs = BumpMap::with_capacity_in(1, arena);
subs.insert(from, to); subs.insert(from, to);
// TODO clean this up // TODO clean this up
@ -4856,7 +4862,7 @@ fn substitute_in_exprs<'a>(arena: &'a Bump, stmt: &mut Stmt<'a>, from: Symbol, t
fn substitute_in_stmt_help<'a>( fn substitute_in_stmt_help<'a>(
arena: &'a Bump, arena: &'a Bump,
stmt: &'a Stmt<'a>, stmt: &'a Stmt<'a>,
subs: &MutMap<Symbol, Symbol>, subs: &BumpMap<Symbol, Symbol>,
) -> Option<&'a Stmt<'a>> { ) -> Option<&'a Stmt<'a>> {
use Stmt::*; use Stmt::*;
@ -5024,7 +5030,7 @@ fn substitute_in_stmt_help<'a>(
fn substitute_in_call<'a>( fn substitute_in_call<'a>(
arena: &'a Bump, arena: &'a Bump,
call: &'a Call<'a>, call: &'a Call<'a>,
subs: &MutMap<Symbol, Symbol>, subs: &BumpMap<Symbol, Symbol>,
) -> Option<Call<'a>> { ) -> Option<Call<'a>> {
let Call { let Call {
call_type, call_type,
@ -5087,7 +5093,7 @@ fn substitute_in_call<'a>(
fn substitute_in_expr<'a>( fn substitute_in_expr<'a>(
arena: &'a Bump, arena: &'a Bump,
expr: &'a Expr<'a>, expr: &'a Expr<'a>,
subs: &MutMap<Symbol, Symbol>, subs: &BumpMap<Symbol, Symbol>,
) -> Option<Expr<'a>> { ) -> Option<Expr<'a>> {
use Expr::*; use Expr::*;
@ -6090,7 +6096,7 @@ fn call_by_name<'a>(
let iter = loc_args.into_iter().rev().zip(field_symbols.iter().rev()); let iter = loc_args.into_iter().rev().zip(field_symbols.iter().rev());
assign_to_symbols(env, procs, layout_cache, iter, result) assign_to_symbols(env, procs, layout_cache, iter, result)
} else { } else {
let pending = PendingSpecialization::from_var(env.subs, fn_var); let pending = PendingSpecialization::from_var(env.arena, env.subs, fn_var);
// When requested (that is, when procs.pending_specializations is `Some`), // When requested (that is, when procs.pending_specializations is `Some`),
// store a pending specialization rather than specializing immediately. // store a pending specialization rather than specializing immediately.
@ -6927,7 +6933,7 @@ fn from_can_pattern_help<'a>(
// sorted fields based on the destruct // sorted fields based on the destruct
let mut mono_destructs = Vec::with_capacity_in(destructs.len(), env.arena); let mut mono_destructs = Vec::with_capacity_in(destructs.len(), env.arena);
let destructs_by_label = env.arena.alloc(MutMap::default()); let mut destructs_by_label = BumpMap::with_capacity_in(destructs.len(), env.arena);
destructs_by_label.extend(destructs.iter().map(|x| (&x.value.label, x))); destructs_by_label.extend(destructs.iter().map(|x| (&x.value.label, x)));
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena); let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);

View file

@ -104,6 +104,8 @@ e.g. you have a test `calculate_sum_test` that only uses the function `add`, whe
* 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 :( . * 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 :( .
* Regex-like find and substitution based on plain english description and example (replacement). i.e. replace all `[` between double quotes with `{`. [Inspiration](https://alexmoltzau.medium.com/english-to-regex-thanks-to-gpt-3-13f03b68236e). * Regex-like find and substitution based on plain english description and example (replacement). i.e. replace all `[` between double quotes with `{`. [Inspiration](https://alexmoltzau.medium.com/english-to-regex-thanks-to-gpt-3-13f03b68236e).
* Show productivity tips based on behavior. i.e. if the user is scrolling through the error bar and clicking on the next error several times, show a tip with "go to next error" shortcut. * Show productivity tips based on behavior. i.e. if the user is scrolling through the error bar and clicking on the next error several times, show a tip with "go to next error" shortcut.
* Command to "benchmark this function" or "benchmark this test" with flamegraph and execution time per line.
#### Autocomplete #### Autocomplete
- Use more space for autocomplete options: - Use more space for autocomplete options:

View file

@ -130,6 +130,9 @@ pub enum EdError {
#[snafu(display("RecordWithoutFields: expected record to have at least one field because it is not an EmpyRecord."))] #[snafu(display("RecordWithoutFields: expected record to have at least one field because it is not an EmpyRecord."))]
RecordWithoutFields { backtrace: Backtrace }, RecordWithoutFields { backtrace: Backtrace },
#[snafu(display("StringParseError: {}", msg))]
StringParseError { msg: String, backtrace: Backtrace },
#[snafu(display("UIError: {}", msg))] #[snafu(display("UIError: {}", msg))]
UIErrorBacktrace { msg: String, backtrace: Backtrace }, UIErrorBacktrace { msg: String, backtrace: Backtrace },
} }

View file

@ -126,6 +126,10 @@ impl<'a> EdModel<'a> {
Ok(prev_id_opt) Ok(prev_id_opt)
} }
pub fn node_exists_at_caret(&self) -> bool {
self.grid_node_map.node_exists_at_pos(self.get_caret())
}
} }
#[derive(Debug)] #[derive(Debug)]

View file

@ -2,7 +2,6 @@
use crate::editor::code_lines::CodeLines; use crate::editor::code_lines::CodeLines;
use crate::editor::ed_error::from_ui_res; use crate::editor::ed_error::from_ui_res;
use crate::editor::ed_error::print_ui_err;
use crate::editor::ed_error::EdResult; use crate::editor::ed_error::EdResult;
use crate::editor::ed_error::MissingSelection; use crate::editor::ed_error::MissingSelection;
use crate::editor::grid_node_map::GridNodeMap; use crate::editor::grid_node_map::GridNodeMap;
@ -12,6 +11,8 @@ use crate::editor::markup::nodes::MarkupNode;
use crate::editor::mvc::app_update::InputOutcome; use crate::editor::mvc::app_update::InputOutcome;
use crate::editor::mvc::ed_model::EdModel; use crate::editor::mvc::ed_model::EdModel;
use crate::editor::mvc::ed_model::SelectedExpression; use crate::editor::mvc::ed_model::SelectedExpression;
use crate::editor::mvc::int_update::start_new_int;
use crate::editor::mvc::int_update::update_int;
use crate::editor::mvc::lookup_update::update_invalid_lookup; use crate::editor::mvc::lookup_update::update_invalid_lookup;
use crate::editor::mvc::record_update::start_new_record; use crate::editor::mvc::record_update::start_new_record;
use crate::editor::mvc::record_update::update_empty_record; use crate::editor::mvc::record_update::update_empty_record;
@ -39,6 +40,7 @@ use crate::ui::text::text_pos::TextPos;
use crate::ui::text::{lines, lines::Lines, lines::SelectableLines}; use crate::ui::text::{lines, lines::Lines, lines::SelectableLines};
use crate::ui::ui_error::UIResult; use crate::ui::ui_error::UIResult;
use crate::window::keyboard_input::Modifiers; use crate::window::keyboard_input::Modifiers;
use bumpalo::Bump;
use roc_can::expected::Expected; use roc_can::expected::Expected;
use roc_collections::all::MutMap; use roc_collections::all::MutMap;
use roc_module::ident::Lowercase; use roc_module::ident::Lowercase;
@ -160,6 +162,33 @@ impl<'a> EdModel<'a> {
self.code_lines.del_at_line(line_nr, index) self.code_lines.del_at_line(line_nr, index)
} }
pub fn set_selected_expr(
&mut self,
expr_start_pos: TextPos,
expr_end_pos: TextPos,
ast_node_id: NodeId<Expr2>,
mark_node_id: MarkNodeId,
) -> EdResult<()> {
self.set_raw_sel(RawSelection {
start_pos: expr_start_pos,
end_pos: expr_end_pos,
})?;
self.set_caret(expr_start_pos);
//let type_str = self.expr2_to_type(ast_node_id);
self.selected_expr_opt = Some(SelectedExpression {
ast_node_id,
mark_node_id,
type_str: PoolStr::new("{}", self.module.env.pool), // TODO get this PoolStr from type inference
});
self.dirty = true;
Ok(())
}
// select all MarkupNodes that refer to specific ast node and its children. // select all MarkupNodes that refer to specific ast node and its children.
pub fn select_expr(&mut self) -> EdResult<()> { pub fn select_expr(&mut self) -> EdResult<()> {
// include parent in selection if an `Expr2` was already selected // include parent in selection if an `Expr2` was already selected
@ -174,22 +203,7 @@ impl<'a> EdModel<'a> {
.grid_node_map .grid_node_map
.get_nested_start_end_pos(parent_id, self)?; .get_nested_start_end_pos(parent_id, self)?;
self.set_raw_sel(RawSelection { self.set_selected_expr(expr_start_pos, expr_end_pos, ast_node_id, parent_id)?;
start_pos: expr_start_pos,
end_pos: expr_end_pos,
})?;
self.set_caret(expr_start_pos);
//let type_str = self.expr2_to_type(ast_node_id);
self.selected_expr_opt = Some(SelectedExpression {
ast_node_id,
mark_node_id: parent_id,
type_str: PoolStr::new("{}", self.module.env.pool), // TODO get this PoolStr from type inference
});
self.dirty = true;
} }
} else { } else {
// select `Expr2` in which caret is currently positioned // select `Expr2` in which caret is currently positioned
@ -198,22 +212,17 @@ impl<'a> EdModel<'a> {
let (expr_start_pos, expr_end_pos, ast_node_id, mark_node_id) = self let (expr_start_pos, expr_end_pos, ast_node_id, mark_node_id) = self
.grid_node_map .grid_node_map
.get_expr_start_end_pos(self.get_caret(), &self)?; .get_expr_start_end_pos(self.get_caret(), &self)?;
self.set_raw_sel(RawSelection {
start_pos: expr_start_pos,
end_pos: expr_end_pos,
})?;
self.set_caret(expr_start_pos); self.set_selected_expr(expr_start_pos, expr_end_pos, ast_node_id, mark_node_id)?;
} else if self
.grid_node_map
.node_exists_at_pos(caret_pos.decrement_col())
{
let (expr_start_pos, expr_end_pos, ast_node_id, mark_node_id) = self
.grid_node_map
.get_expr_start_end_pos(self.get_caret().decrement_col(), &self)?;
//let type_str = self.expr2_to_type(ast_node_id); self.set_selected_expr(expr_start_pos, expr_end_pos, ast_node_id, mark_node_id)?;
self.selected_expr_opt = Some(SelectedExpression {
ast_node_id,
mark_node_id,
type_str: PoolStr::new("{}", self.module.env.pool), // TODO get this PoolStr from type inference
});
self.dirty = true;
} }
} }
@ -223,8 +232,10 @@ impl<'a> EdModel<'a> {
fn expr2_to_type(&mut self, expr2_id: NodeId<Expr2>) -> PoolStr { fn expr2_to_type(&mut self, expr2_id: NodeId<Expr2>) -> PoolStr {
let var = self.module.env.var_store.fresh(); let var = self.module.env.var_store.fresh();
let expr = self.module.env.pool.get(expr2_id); let expr = self.module.env.pool.get(expr2_id);
let arena = Bump::new();
let constrained = constrain_expr( let constrained = constrain_expr(
&arena,
&mut self.module.env, &mut self.module.env,
&expr, &expr,
Expected::NoExpectation(Type2::Variable(var)), Expected::NoExpectation(Type2::Variable(var)),
@ -542,11 +553,9 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult
InputOutcome::Accepted InputOutcome::Accepted
} }
ch => { ch => {
let curr_mark_node_id_res = ed_model.get_curr_mark_node_id();
let outcome = let outcome =
match curr_mark_node_id_res { if ed_model.node_exists_at_caret() {
Ok(curr_mark_node_id) => { let curr_mark_node_id = ed_model.get_curr_mark_node_id()?;
let curr_mark_node = ed_model.markup_node_pool.get(curr_mark_node_id); let curr_mark_node = ed_model.markup_node_pool.get(curr_mark_node_id);
let prev_mark_node_id_opt = ed_model.get_prev_mark_node_id()?; let prev_mark_node_id_opt = ed_model.get_prev_mark_node_id()?;
@ -561,6 +570,9 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult
'{' => { '{' => {
start_new_record(ed_model)? start_new_record(ed_model)?
} }
'0'..='9' => {
start_new_int(ed_model, ch)?
}
_ => InputOutcome::Ignored _ => InputOutcome::Ignored
} }
} else if let Some(prev_mark_node_id) = prev_mark_node_id_opt{ } else if let Some(prev_mark_node_id) = prev_mark_node_id_opt{
@ -602,68 +614,78 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult
InputOutcome::Ignored InputOutcome::Ignored
} }
} }
Expr2::SmallInt{ .. } => {
update_int(ed_model, curr_mark_node_id, ch)?
}
_ => InputOutcome::Ignored _ => InputOutcome::Ignored
} }
} else if ch.is_ascii_alphanumeric() { // prev_mark_node_id != curr_mark_node_id } else if ch.is_ascii_alphanumeric() { // prev_mark_node_id != curr_mark_node_id
let prev_ast_node_id =
ed_model
.markup_node_pool
.get(prev_mark_node_id)
.get_ast_node_id();
let prev_node_ref = ed_model.module.env.pool.get(prev_ast_node_id); match ast_node_ref {
Expr2::SmallInt{ .. } => {
match prev_node_ref { update_int(ed_model, curr_mark_node_id, ch)?
Expr2::InvalidLookup(old_pool_str) => {
update_invalid_lookup(
&ch.to_string(),
old_pool_str,
prev_mark_node_id,
prev_ast_node_id,
ed_model
)?
}
Expr2::Record{ record_var:_, fields } => {
let prev_mark_node = ed_model.markup_node_pool.get(prev_mark_node_id);
if (curr_mark_node.get_content()? == nodes::RIGHT_ACCOLADE || curr_mark_node.get_content()? == nodes::COLON) &&
prev_mark_node.is_all_alphanumeric()? {
update_record_field(
&ch.to_string(),
ed_model.get_caret(),
prev_mark_node_id,
fields,
ed_model,
)?
} else if prev_mark_node.get_content()? == nodes::LEFT_ACCOLADE && curr_mark_node.is_all_alphanumeric()? {
update_record_field(
&ch.to_string(),
ed_model.get_caret(),
curr_mark_node_id,
fields,
ed_model,
)?
} else {
InputOutcome::Ignored
}
} }
_ => { _ => {
match ast_node_ref { let prev_ast_node_id =
Expr2::EmptyRecord => { ed_model
let sibling_ids = curr_mark_node.get_sibling_ids(&ed_model.markup_node_pool); .markup_node_pool
.get(prev_mark_node_id)
.get_ast_node_id();
if ch.is_ascii_alphabetic() && ch.is_ascii_lowercase() { let prev_node_ref = ed_model.module.env.pool.get(prev_ast_node_id);
update_empty_record(
match prev_node_ref {
Expr2::InvalidLookup(old_pool_str) => {
update_invalid_lookup(
&ch.to_string(),
old_pool_str,
prev_mark_node_id,
prev_ast_node_id,
ed_model
)?
}
Expr2::Record{ record_var:_, fields } => {
let prev_mark_node = ed_model.markup_node_pool.get(prev_mark_node_id);
if (curr_mark_node.get_content()? == nodes::RIGHT_ACCOLADE || curr_mark_node.get_content()? == nodes::COLON) &&
prev_mark_node.is_all_alphanumeric()? {
update_record_field(
&ch.to_string(), &ch.to_string(),
ed_model.get_caret(),
prev_mark_node_id, prev_mark_node_id,
sibling_ids, fields,
ed_model ed_model,
)?
} else if prev_mark_node.get_content()? == nodes::LEFT_ACCOLADE && curr_mark_node.is_all_alphanumeric()? {
update_record_field(
&ch.to_string(),
ed_model.get_caret(),
curr_mark_node_id,
fields,
ed_model,
)? )?
} else { } else {
InputOutcome::Ignored InputOutcome::Ignored
} }
} }
_ => InputOutcome::Ignored Expr2::SmallInt{ .. } => {
update_int(ed_model, prev_mark_node_id, ch)?
}
_ => {
match ast_node_ref {
Expr2::EmptyRecord => {
let sibling_ids = curr_mark_node.get_sibling_ids(&ed_model.markup_node_pool);
update_empty_record(
&ch.to_string(),
prev_mark_node_id,
sibling_ids,
ed_model
)?
}
_ => InputOutcome::Ignored
}
}
} }
} }
} }
@ -682,15 +704,35 @@ pub fn handle_new_char(received_char: &char, ed_model: &mut EdModel) -> EdResult
} }
} else { } else {
// Not supporting any Expr2 right now that would allow prepending at the start of a line match ast_node_ref {
Expr2::SmallInt{ .. } => {
update_int(ed_model, curr_mark_node_id, ch)?
},
// only SmallInt currently allows prepending at the start
_ => InputOutcome::Ignored
}
}
} else { //no MarkupNode at the current position
let prev_mark_node_id_opt = ed_model.get_prev_mark_node_id()?;
if let Some(prev_mark_node_id) = prev_mark_node_id_opt {
let prev_mark_node = ed_model.markup_node_pool.get(prev_mark_node_id);
let prev_ast_node = ed_model.module.env.pool.get(prev_mark_node.get_ast_node_id());
match prev_ast_node {
Expr2::SmallInt{ .. } => {
update_int(ed_model, prev_mark_node_id, ch)?
},
_ => {
InputOutcome::Ignored
}
}
} else {
InputOutcome::Ignored InputOutcome::Ignored
} }
}, };
Err(e) => {
print_ui_err(&e);
InputOutcome::Ignored
}
};
if let InputOutcome::Accepted = outcome { if let InputOutcome::Accepted = outcome {
ed_model.set_sel_none(); ed_model.set_sel_none();
@ -748,6 +790,10 @@ pub mod test_ed_update {
assert_insert_seq(pre_lines, expected_post_lines, &new_char.to_string()) assert_insert_seq(pre_lines, expected_post_lines, &new_char.to_string())
} }
pub fn assert_insert_ignore(lines: &[&str], new_char: char) -> Result<(), String> {
assert_insert_seq(lines, lines, &new_char.to_string())
}
// Create ed_model from pre_lines DSL, do handle_new_char() for every char in new_char_seq, check if modified ed_model has expected // Create ed_model from pre_lines DSL, do handle_new_char() for every char in new_char_seq, check if modified ed_model has expected
// string representation of code, caret position and active selection. // string representation of code, caret position and active selection.
pub fn assert_insert_seq( pub fn assert_insert_seq(
@ -782,13 +828,69 @@ pub mod test_ed_update {
// space is added because Blank is inserted // space is added because Blank is inserted
assert_insert(&[""], &[""], 'a')?; assert_insert(&[""], &[""], 'a')?;
assert_insert(&[""], &[""], ';')?; assert_insert(&[""], &[""], ';')?;
assert_insert(&[""], &[""], '5')?;
assert_insert(&[""], &[""], '-')?; assert_insert(&[""], &[""], '-')?;
assert_insert(&[""], &[""], '_')?; assert_insert(&[""], &[""], '_')?;
Ok(()) Ok(())
} }
#[test]
fn test_int() -> Result<(), String> {
assert_insert(&[""], &["0┃"], '0')?;
assert_insert(&[""], &["1┃"], '1')?;
assert_insert(&[""], &["2┃"], '2')?;
assert_insert(&[""], &["3┃"], '3')?;
assert_insert(&[""], &["4┃"], '4')?;
assert_insert(&[""], &["5┃"], '5')?;
assert_insert(&[""], &["6┃"], '6')?;
assert_insert(&[""], &["7┃"], '7')?;
assert_insert(&[""], &["8┃"], '8')?;
assert_insert(&[""], &["9┃"], '9')?;
assert_insert(&["1┃"], &["19┃"], '9')?;
assert_insert(&["9876┃"], &["98769┃"], '9')?;
assert_insert(&["10┃"], &["103┃"], '3')?;
assert_insert(&["┃0"], &["1┃0"], '1')?;
assert_insert(&["10000┃"], &["100000┃"], '0')?;
assert_insert(&["┃1234"], &["5┃1234"], '5')?;
assert_insert(&["1┃234"], &["10┃234"], '0')?;
assert_insert(&["12┃34"], &["121┃34"], '1')?;
assert_insert(&["123┃4"], &["1232┃4"], '2')?;
Ok(())
}
#[test]
fn test_ignore_int() -> Result<(), String> {
assert_insert_seq_ignore(&["┃0"], "{}()[]-><-_\"azAZ:@")?;
assert_insert_seq_ignore(&["┃7"], "{}()[]-><-_\"azAZ:@")?;
assert_insert_seq_ignore(&["0┃"], ",{}()[]-><-_\"azAZ:@")?;
assert_insert_seq_ignore(&["8┃"], ",{}()[]-><-_\"azAZ:@")?;
assert_insert_seq_ignore(&["20┃"], ",{}()[]-><-_\"azAZ:@")?;
assert_insert_seq_ignore(&["83┃"], ",{}()[]-><-_\"azAZ:@")?;
assert_insert_seq_ignore(&["1┃0"], ",{}()[]-><-_\"azAZ:@")?;
assert_insert_seq_ignore(&["8┃4"], ",{}()[]-><-_\"azAZ:@")?;
assert_insert_seq_ignore(&["┃10"], ",{}()[]-><-_\"azAZ:@")?;
assert_insert_seq_ignore(&["┃84"], ",{}()[]-><-_\"azAZ:@")?;
assert_insert_seq_ignore(&["129┃96"], ",{}()[]-><-_\"azAZ:@")?;
assert_insert_seq_ignore(&["97┃684"], ",{}()[]-><-_\"azAZ:@")?;
assert_insert_ignore(&["0┃"], '0')?;
assert_insert_ignore(&["0┃"], '9')?;
assert_insert_ignore(&["┃0"], '0')?;
assert_insert_ignore(&["┃1234"], '0')?;
assert_insert_ignore(&["┃100"], '0')?;
Ok(())
}
//TODO test_int arch bit limit
#[test] #[test]
fn test_string() -> Result<(), String> { fn test_string() -> Result<(), String> {
assert_insert(&[""], &["\"\""], '"')?; assert_insert(&[""], &["\"\""], '"')?;
@ -923,14 +1025,29 @@ pub mod test_ed_update {
assert_insert_seq(&["{ a┃ }"], &["{ a: \"\" }"], ":\"")?; assert_insert_seq(&["{ a┃ }"], &["{ a: \"\" }"], ":\"")?;
assert_insert_seq(&["{ abc┃ }"], &["{ abc: \"\" }"], ":\"")?; assert_insert_seq(&["{ abc┃ }"], &["{ abc: \"\" }"], ":\"")?;
assert_insert_seq(&["{ a┃ }"], &["{ a: 0┃ }"], ":0")?;
assert_insert_seq(&["{ abc┃ }"], &["{ abc: 9┃ }"], ":9")?;
assert_insert_seq(&["{ a┃ }"], &["{ a: 1000┃ }"], ":1000")?;
assert_insert_seq(&["{ abc┃ }"], &["{ abc: 98761┃ }"], ":98761")?;
assert_insert(&["{ a: \"\" }"], &["{ a: \"a┃\" }"], 'a')?; assert_insert(&["{ a: \"\" }"], &["{ a: \"a┃\" }"], 'a')?;
assert_insert(&["{ a: \"a┃\" }"], &["{ a: \"ab┃\" }"], 'b')?; assert_insert(&["{ a: \"a┃\" }"], &["{ a: \"ab┃\" }"], 'b')?;
assert_insert(&["{ a: \"a┃b\" }"], &["{ a: \"az┃b\" }"], 'z')?; assert_insert(&["{ a: \"a┃b\" }"], &["{ a: \"az┃b\" }"], 'z')?;
assert_insert(&["{ a: \"┃ab\" }"], &["{ a: \"z┃ab\" }"], 'z')?; assert_insert(&["{ a: \"┃ab\" }"], &["{ a: \"z┃ab\" }"], 'z')?;
assert_insert(&["{ a: 1┃ }"], &["{ a: 10┃ }"], '0')?;
assert_insert(&["{ a: 100┃ }"], &["{ a: 1004┃ }"], '4')?;
assert_insert(&["{ a: 9┃76 }"], &["{ a: 98┃76 }"], '8')?;
assert_insert(&["{ a: 4┃691 }"], &["{ a: 40┃691 }"], '0')?;
assert_insert(&["{ a: 469┃1 }"], &["{ a: 4699┃1 }"], '9')?;
assert_insert(&["{ camelCase: \"\" }"], &["{ camelCase: \"a┃\" }"], 'a')?; assert_insert(&["{ camelCase: \"\" }"], &["{ camelCase: \"a┃\" }"], 'a')?;
assert_insert(&["{ camelCase: \"a┃\" }"], &["{ camelCase: \"ab┃\" }"], 'b')?; assert_insert(&["{ camelCase: \"a┃\" }"], &["{ camelCase: \"ab┃\" }"], 'b')?;
assert_insert(&["{ camelCase: 3┃ }"], &["{ camelCase: 35┃ }"], '5')?;
assert_insert(&["{ camelCase: ┃2 }"], &["{ camelCase: 5┃2 }"], '5')?;
assert_insert(&["{ camelCase: 10┃2 }"], &["{ camelCase: 106┃2 }"], '6')?;
assert_insert(&["{ a┃: \"\" }"], &["{ ab┃: \"\" }"], 'b')?; assert_insert(&["{ a┃: \"\" }"], &["{ ab┃: \"\" }"], 'b')?;
assert_insert(&["{ ┃a: \"\" }"], &["{ z┃a: \"\" }"], 'z')?; assert_insert(&["{ ┃a: \"\" }"], &["{ z┃a: \"\" }"], 'z')?;
assert_insert(&["{ ab┃: \"\" }"], &["{ abc┃: \"\" }"], 'c')?; assert_insert(&["{ ab┃: \"\" }"], &["{ abc┃: \"\" }"], 'c')?;
@ -951,7 +1068,16 @@ pub mod test_ed_update {
'z', 'z',
)?; )?;
assert_insert(&["{ a┃: 0 }"], &["{ ab┃: 0 }"], 'b')?;
assert_insert(&["{ ┃a: 2100 }"], &["{ z┃a: 2100 }"], 'z')?;
assert_insert(&["{ ab┃: 9876 }"], &["{ abc┃: 9876 }"], 'c')?;
assert_insert(&["{ ┃ab: 102 }"], &["{ z┃ab: 102 }"], 'z')?;
assert_insert(&["{ camelCase┃: 99999 }"], &["{ camelCaseB┃: 99999 }"], 'B')?;
assert_insert(&["{ camel┃Case: 88156 }"], &["{ camelZ┃Case: 88156 }"], 'Z')?;
assert_insert(&["{ ┃camelCase: 1 }"], &["{ z┃camelCase: 1 }"], 'z')?;
assert_insert_seq(&[""], &["{ camelCase: \"hello┃\" }"], "{camelCase:\"hello")?; assert_insert_seq(&[""], &["{ camelCase: \"hello┃\" }"], "{camelCase:\"hello")?;
assert_insert_seq(&[""], &["{ camelCase: 10009┃ }"], "{camelCase:10009")?;
Ok(()) Ok(())
} }
@ -1013,6 +1139,25 @@ pub mod test_ed_update {
"ul", "ul",
)?; )?;
assert_insert_seq(&["{ a: { zulu┃ } }"], &["{ a: { zulu: 1┃ } }"], ":1")?;
assert_insert_seq(
&["{ abc: { camelCase┃ } }"],
&["{ abc: { camelCase: 0┃ } }"],
":0",
)?;
assert_insert_seq(
&["{ camelCase: { z┃ } }"],
&["{ camelCase: { z: 45┃ } }"],
":45",
)?;
assert_insert_seq(&["{ a: { zulu: ┃0 } }"], &["{ a: { zulu: 4┃0 } }"], "4")?;
assert_insert_seq(
&["{ a: { zulu: 10┃98 } }"],
&["{ a: { zulu: 1077┃98 } }"],
"77",
)?;
assert_insert_seq(&["{ a: { zulu┃ } }"], &["{ a: { zulu: { ┃ } } }"], ":{")?; assert_insert_seq(&["{ a: { zulu┃ } }"], &["{ a: { zulu: { ┃ } } }"], ":{")?;
assert_insert_seq( assert_insert_seq(
&["{ abc: { camelCase┃ } }"], &["{ abc: { camelCase┃ } }"],
@ -1071,146 +1216,199 @@ pub mod test_ed_update {
Ok(()) Ok(())
} }
const IGNORE_CHARS: &str = "{}()[]-><-_\"azAZ:@09";
const IGNORE_NO_LTR: &str = "{\"5";
const IGNORE_NO_NUM: &str = "a{\"";
#[test] #[test]
fn test_ignore_record() -> Result<(), String> { fn test_ignore_record() -> Result<(), String> {
assert_insert_seq_ignore(&["┃{ }"], "a{\"5")?; assert_insert_seq_ignore(&["┃{ }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ }┃"], "a{\"5")?; assert_insert_seq_ignore(&["{ }┃"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ }"], "a{\"5")?; assert_insert_seq_ignore(&["{┃ }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ ┃}"], "a{\"5")?; assert_insert_seq_ignore(&["{ ┃}"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ ┃ }"], "{\"5")?; assert_insert_seq_ignore(&["{ ┃ }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ ┃a }"], "{\"5")?; assert_insert_seq_ignore(&["{ ┃a }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ ┃abc }"], "{\"5")?; assert_insert_seq_ignore(&["{ ┃abc }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["┃{ a }"], "a{\"5")?; assert_insert_seq_ignore(&["┃{ a }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a }┃"], "a{\"5")?; assert_insert_seq_ignore(&["{ a }┃"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ a }"], "a{\"5")?; assert_insert_seq_ignore(&["{┃ a }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a ┃}"], "a{\"5")?; assert_insert_seq_ignore(&["{ a ┃}"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["┃{ a15 }"], "a{\"5")?; assert_insert_seq_ignore(&["┃{ a15 }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a15 }┃"], "a{\"5")?; assert_insert_seq_ignore(&["{ a15 }┃"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ a15 }"], "a{\"5")?; assert_insert_seq_ignore(&["{┃ a15 }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a15 ┃}"], "a{\"5")?; assert_insert_seq_ignore(&["{ a15 ┃}"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["┃{ camelCase }"], "a{\"5")?; assert_insert_seq_ignore(&["┃{ camelCase }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ camelCase }┃"], "a{\"5")?; assert_insert_seq_ignore(&["{ camelCase }┃"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ camelCase }"], "a{\"5")?; assert_insert_seq_ignore(&["{┃ camelCase }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ camelCase ┃}"], "a{\"5")?; assert_insert_seq_ignore(&["{ camelCase ┃}"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["┃{ a: \"\" }"], "a{\"5")?; assert_insert_seq_ignore(&["┃{ a: \"\" }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ a: \"\" }"], "a{\"5")?; assert_insert_seq_ignore(&["{┃ a: \"\" }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a: ┃\"\" }"], "a{\"5")?; assert_insert_seq_ignore(&["{ a: ┃\"\" }"], "0")?;
assert_insert_seq_ignore(&["{ a: \"\"┃ }"], "a{\"5")?; assert_insert_seq_ignore(&["{ a: ┃\"\" }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a: \"\" }┃"], "a{\"5")?; assert_insert_seq_ignore(&["{ a: \"\"┃ }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a: \"\" }┃"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["┃{ camelCase: \"\" }"], "a{\"5")?; assert_insert_seq_ignore(&["┃{ a: 1 }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ camelCase: \"\" }"], "a{\"5")?; assert_insert_seq_ignore(&["{┃ a: 2 }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ camelCase: ┃\"\" }"], "a{\"5")?; assert_insert_seq_ignore(&["{ a: ┃6 }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ camelCase: \"\"┃ }"], "a{\"5")?; assert_insert_seq_ignore(&["{ a: 8┃ }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ camelCase: \"\" }┃"], "a{\"5")?; assert_insert_seq_ignore(&["{ a: 0 }┃"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["┃{ a: \"z\" }"], "a{\"5")?; assert_insert_seq_ignore(&["┃{ camelCase: 1 }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ a: \"z\" }"], "a{\"5")?; assert_insert_seq_ignore(&["{┃ camelCase: 7 }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a: ┃\"z\" }"], "a{\"5")?; assert_insert_seq_ignore(&["{ camelCase: ┃2 }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ a: \"z\"┃ }"], "a{\"5")?; assert_insert_seq_ignore(&["{ camelCase: 4┃ }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ a: \"z\" }┃"], "a{\"5")?; assert_insert_seq_ignore(&["{ camelCase: 9 }┃"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["┃{ a: \"hello, hello.0123456789ZXY{}[]-><-\" }"], "a{\"5")?; assert_insert_seq_ignore(&["┃{ camelCase: \"\" }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ a: \"hello, hello.0123456789ZXY{}[]-><-\" }"], "a{\"5")?; assert_insert_seq_ignore(&["{┃ camelCase: \"\" }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a: ┃\"hello, hello.0123456789ZXY{}[]-><-\" }"], "a{\"5")?; assert_insert_seq_ignore(&["{ camelCase: ┃\"\" }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a: \"hello, hello.0123456789ZXY{}[]-><-\"┃ }"], "a{\"5")?; assert_insert_seq_ignore(&["{ camelCase: \"\"┃ }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a: \"hello, hello.0123456789ZXY{}[]-><-\" }┃"], "a{\"5")?; assert_insert_seq_ignore(&["{ camelCase: \"\" }┃"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["┃{ a: \"z\" }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ a: \"z\" }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a: ┃\"z\" }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a: \"z\"┃ }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a: \"z\" }┃"], IGNORE_CHARS)?;
assert_insert_seq_ignore(
&["┃{ a: \"hello, hello.0123456789ZXY{}[]-><-\" }"],
IGNORE_CHARS,
)?;
assert_insert_seq_ignore(
&["{┃ a: \"hello, hello.0123456789ZXY{}[]-><-\" }"],
IGNORE_CHARS,
)?;
assert_insert_seq_ignore(
&["{ a: ┃\"hello, hello.0123456789ZXY{}[]-><-\" }"],
IGNORE_CHARS,
)?;
assert_insert_seq_ignore(
&["{ a: \"hello, hello.0123456789ZXY{}[]-><-\"┃ }"],
IGNORE_CHARS,
)?;
assert_insert_seq_ignore(
&["{ a: \"hello, hello.0123456789ZXY{}[]-><-\" }┃"],
IGNORE_CHARS,
)?;
assert_insert_seq_ignore(&["┃{ a: 915480 }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{┃ a: 915480 }"], IGNORE_CHARS)?;
assert_insert_seq_ignore(&["{ a: ┃915480 }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ a: 915480┃ }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ a: 915480 }┃"], IGNORE_CHARS)?;
Ok(()) Ok(())
} }
#[test] #[test]
fn test_ignore_nested_record() -> Result<(), String> { fn test_ignore_nested_record() -> Result<(), String> {
assert_insert_seq_ignore(&["{ a: { ┃ } }"], "{\"5")?; assert_insert_seq_ignore(&["{ a: { ┃ } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ a: ┃{ } }"], "{\"5")?; assert_insert_seq_ignore(&["{ a: ┃{ } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ a: {┃ } }"], "{\"5")?; assert_insert_seq_ignore(&["{ a: {┃ } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ a: { }┃ }"], "{\"5")?; assert_insert_seq_ignore(&["{ a: { }┃ }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ a: { } ┃}"], "{\"5")?; assert_insert_seq_ignore(&["{ a: { } ┃}"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ a: { } }┃"], "{\"5")?; assert_insert_seq_ignore(&["{ a: { } }┃"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ a:┃ { } }"], "{\"5")?; assert_insert_seq_ignore(&["{ a:┃ { } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{┃ a: { } }"], "{\"5")?; assert_insert_seq_ignore(&["{┃ a: { } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["┃{ a: { } }"], "{\"5")?; assert_insert_seq_ignore(&["┃{ a: { } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ ┃a: { } }"], "1")?; assert_insert_seq_ignore(&["{ ┃a: { } }"], "1")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a ┃} }"], "{\"5")?; assert_insert_seq_ignore(&["{ camelCaseB1: { z15a ┃} }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: {┃ z15a } }"], "{\"5")?; assert_insert_seq_ignore(&["{ camelCaseB1: {┃ z15a } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: ┃{ z15a } }"], "{\"5")?; assert_insert_seq_ignore(&["{ camelCaseB1: ┃{ z15a } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a }┃ }"], "{\"5")?; assert_insert_seq_ignore(&["{ camelCaseB1: { z15a }┃ }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a } ┃}"], "{\"5")?; assert_insert_seq_ignore(&["{ camelCaseB1: { z15a } ┃}"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a } }┃"], "{\"5")?; assert_insert_seq_ignore(&["{ camelCaseB1: { z15a } }┃"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1:┃ { z15a } }"], "{\"5")?; assert_insert_seq_ignore(&["{ camelCaseB1:┃ { z15a } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{┃ camelCaseB1: { z15a } }"], "{\"5")?; assert_insert_seq_ignore(&["{┃ camelCaseB1: { z15a } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["┃{ camelCaseB1: { z15a } }"], "{\"5")?; assert_insert_seq_ignore(&["┃{ camelCaseB1: { z15a } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ ┃camelCaseB1: { z15a } }"], "1")?; assert_insert_seq_ignore(&["{ ┃camelCaseB1: { z15a } }"], "1")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { ┃z15a } }"], "1")?; assert_insert_seq_ignore(&["{ camelCaseB1: { ┃z15a } }"], "1")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: \"\"┃ } }"], "{\"5")?; assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: \"\"┃ } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: ┃\"\" } }"], "{\"5")?; assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: ┃\"\" } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a:┃ \"\" } }"], "{\"5")?; assert_insert_seq_ignore(&["{ camelCaseB1: { z15a:┃ \"\" } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: \"\" ┃} }"], "{\"5")?; assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: \"\" ┃} }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: {┃ z15a: \"\" } }"], "{\"5")?; assert_insert_seq_ignore(&["{ camelCaseB1: {┃ z15a: \"\" } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: ┃{ z15a: \"\" } }"], "{\"5")?; assert_insert_seq_ignore(&["{ camelCaseB1: ┃{ z15a: \"\" } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: \"\" }┃ }"], "{\"5")?; assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: \"\" }┃ }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: \"\" } ┃}"], "{\"5")?; assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: \"\" } ┃}"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: \"\" } }┃"], "{\"5")?; assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: \"\" } }┃"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ camelCaseB1:┃ { z15a: \"\" } }"], "{\"5")?; assert_insert_seq_ignore(&["{ camelCaseB1:┃ { z15a: \"\" } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{┃ camelCaseB1: { z15a: \"\" } }"], "{\"5")?; assert_insert_seq_ignore(&["{┃ camelCaseB1: { z15a: \"\" } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["┃{ camelCaseB1: { z15a: \"\" } }"], "{\"5")?; assert_insert_seq_ignore(&["┃{ camelCaseB1: { z15a: \"\" } }"], IGNORE_NO_LTR)?;
assert_insert_seq_ignore(&["{ ┃camelCaseB1: { z15a: \"\" } }"], "1")?; assert_insert_seq_ignore(&["{ ┃camelCaseB1: { z15a: \"\" } }"], "1")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { ┃z15a: \"\" } }"], "1")?; assert_insert_seq_ignore(&["{ camelCaseB1: { ┃z15a: \"\" } }"], "1")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: 0┃ } }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: ┃123 } }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a:┃ 999 } }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: 80 ┃} }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ camelCaseB1: {┃ z15a: 99000 } }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ camelCaseB1: ┃{ z15a: 12 } }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: 7 }┃ }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: 98 } ┃}"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ camelCaseB1: { z15a: 4582 } }┃"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ camelCaseB1:┃ { z15a: 0 } }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{┃ camelCaseB1: { z15a: 44 } }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["┃{ camelCaseB1: { z15a: 100123 } }"], IGNORE_NO_NUM)?;
assert_insert_seq_ignore(&["{ ┃camelCaseB1: { z15a: 5 } }"], "1")?;
assert_insert_seq_ignore(&["{ camelCaseB1: { ┃z15a: 6 } }"], "1")?;
assert_insert_seq_ignore( assert_insert_seq_ignore(
&["{ camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\"┃ } }"], &["{ camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\"┃ } }"],
"{\"5", IGNORE_NO_LTR,
)?; )?;
assert_insert_seq_ignore( assert_insert_seq_ignore(
&["{ camelCaseB1: { z15a: ┃\"hello, hello.0123456789ZXY{}[]-><-\" } }"], &["{ camelCaseB1: { z15a: ┃\"hello, hello.0123456789ZXY{}[]-><-\" } }"],
"{\"5", IGNORE_NO_LTR,
)?; )?;
assert_insert_seq_ignore( assert_insert_seq_ignore(
&["{ camelCaseB1: { z15a:┃ \"hello, hello.0123456789ZXY{}[]-><-\" } }"], &["{ camelCaseB1: { z15a:┃ \"hello, hello.0123456789ZXY{}[]-><-\" } }"],
"{\"5", IGNORE_NO_LTR,
)?; )?;
assert_insert_seq_ignore( assert_insert_seq_ignore(
&["{ camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" ┃} }"], &["{ camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" ┃} }"],
"{\"5", IGNORE_NO_LTR,
)?; )?;
assert_insert_seq_ignore( assert_insert_seq_ignore(
&["{ camelCaseB1: {┃ z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }"], &["{ camelCaseB1: {┃ z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }"],
"{\"5", IGNORE_NO_LTR,
)?; )?;
assert_insert_seq_ignore( assert_insert_seq_ignore(
&["{ camelCaseB1: ┃{ z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }"], &["{ camelCaseB1: ┃{ z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }"],
"{\"5", IGNORE_NO_LTR,
)?; )?;
assert_insert_seq_ignore( assert_insert_seq_ignore(
&["{ camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" }┃ }"], &["{ camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" }┃ }"],
"{\"5", IGNORE_NO_LTR,
)?; )?;
assert_insert_seq_ignore( assert_insert_seq_ignore(
&["{ camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } ┃}"], &["{ camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } ┃}"],
"{\"5", IGNORE_NO_LTR,
)?; )?;
assert_insert_seq_ignore( assert_insert_seq_ignore(
&["{ camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }┃"], &["{ camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }┃"],
"{\"5", IGNORE_NO_LTR,
)?; )?;
assert_insert_seq_ignore( assert_insert_seq_ignore(
&["{ camelCaseB1:┃ { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }"], &["{ camelCaseB1:┃ { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }"],
"{\"5", IGNORE_NO_LTR,
)?; )?;
assert_insert_seq_ignore( assert_insert_seq_ignore(
&["{┃ camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }"], &["{┃ camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }"],
"{\"5", IGNORE_NO_LTR,
)?; )?;
assert_insert_seq_ignore( assert_insert_seq_ignore(
&["┃{ camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }"], &["┃{ camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }"],
"{\"5", IGNORE_NO_LTR,
)?; )?;
assert_insert_seq_ignore( assert_insert_seq_ignore(
&["{ ┃camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }"], &["{ ┃camelCaseB1: { z15a: \"hello, hello.0123456789ZXY{}[]-><-\" } }"],
@ -1223,35 +1421,35 @@ pub mod test_ed_update {
assert_insert_seq_ignore( assert_insert_seq_ignore(
&["{ g: { oi: { ng: { d: { e: { e: { p: { camelCase ┃} } } } } } } }"], &["{ g: { oi: { ng: { d: { e: { e: { p: { camelCase ┃} } } } } } } }"],
"{\"5", IGNORE_NO_LTR,
)?; )?;
assert_insert_seq_ignore( assert_insert_seq_ignore(
&["{ g: { oi: { ng: { d: { e: { e: { p: { camelCase } ┃} } } } } } }"], &["{ g: { oi: { ng: { d: { e: { e: { p: { camelCase } ┃} } } } } } }"],
"{\"5", IGNORE_NO_LTR,
)?; )?;
assert_insert_seq_ignore( assert_insert_seq_ignore(
&["{ g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } } } }┃"], &["{ g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } } } }┃"],
"{\"5", IGNORE_NO_LTR,
)?; )?;
assert_insert_seq_ignore( assert_insert_seq_ignore(
&["{ g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } ┃} } }"], &["{ g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } ┃} } }"],
"{\"5", IGNORE_NO_LTR,
)?; )?;
assert_insert_seq_ignore( assert_insert_seq_ignore(
&["{ g: { oi: { ng: { d: { e: {┃ e: { p: { camelCase } } } } } } } }"], &["{ g: { oi: { ng: { d: { e: {┃ e: { p: { camelCase } } } } } } } }"],
"{\"5", IGNORE_NO_LTR,
)?; )?;
assert_insert_seq_ignore( assert_insert_seq_ignore(
&["{ g: { oi: { ng: { d: { e: { e:┃ { p: { camelCase } } } } } } } }"], &["{ g: { oi: { ng: { d: { e: { e:┃ { p: { camelCase } } } } } } } }"],
"{\"5", IGNORE_NO_LTR,
)?; )?;
assert_insert_seq_ignore( assert_insert_seq_ignore(
&["{┃ g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } } } }"], &["{┃ g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } } } }"],
"{\"5", IGNORE_NO_LTR,
)?; )?;
assert_insert_seq_ignore( assert_insert_seq_ignore(
&["┃{ g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } } } }"], &["┃{ g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } } } }"],
"{\"5", IGNORE_NO_LTR,
)?; )?;
assert_insert_seq_ignore( assert_insert_seq_ignore(
&["{ ┃g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } } } }"], &["{ ┃g: { oi: { ng: { d: { e: { e: { p: { camelCase } } } } } } } }"],
@ -1301,6 +1499,20 @@ pub mod test_ed_update {
Ok(()) Ok(())
} }
#[test]
fn test_ctrl_shift_up_int() -> Result<(), String> {
assert_ctrl_shift_up(&["5┃"], &["5"])?;
assert_ctrl_shift_up_repeat(&["0┃"], &["0"], 3)?;
assert_ctrl_shift_up(&["12345┃"], &["12345"])?;
assert_ctrl_shift_up(&["┃12345"], &["12345"])?;
assert_ctrl_shift_up(&["1┃2345"], &["12345"])?;
assert_ctrl_shift_up(&["12┃345"], &["12345"])?;
assert_ctrl_shift_up(&["123┃45"], &["12345"])?;
assert_ctrl_shift_up(&["1234┃5"], &["12345"])?;
Ok(())
}
#[test] #[test]
fn test_ctrl_shift_up_string() -> Result<(), String> { fn test_ctrl_shift_up_string() -> Result<(), String> {
assert_ctrl_shift_up(&["\"\""], &["┃❮\"\""])?; assert_ctrl_shift_up(&["\"\""], &["┃❮\"\""])?;
@ -1317,8 +1529,8 @@ pub mod test_ed_update {
&["┃❮\"hello, hello.0123456789ZXY{}[]-><-\""], &["┃❮\"hello, hello.0123456789ZXY{}[]-><-\""],
)?; )?;
assert_ctrl_shift_up(&["\"\""], &["\"\""])?; assert_ctrl_shift_up(&["\"\""], &["┃❮\"\""])?;
assert_ctrl_shift_up(&["\"abc\""], &["\"abc\""])?; assert_ctrl_shift_up(&["\"abc\""], &["┃❮\"abc\""])?;
Ok(()) Ok(())
} }
@ -1330,7 +1542,7 @@ pub mod test_ed_update {
assert_ctrl_shift_up(&["┃{ }"], &["┃❮{ }"])?; assert_ctrl_shift_up(&["┃{ }"], &["┃❮{ }"])?;
assert_ctrl_shift_up(&["{ ┃}"], &["┃❮{ }"])?; assert_ctrl_shift_up(&["{ ┃}"], &["┃❮{ }"])?;
assert_ctrl_shift_up_repeat(&["{ ┃ }"], &["┃❮{ }"], 4)?; assert_ctrl_shift_up_repeat(&["{ ┃ }"], &["┃❮{ }"], 4)?;
assert_ctrl_shift_up(&["{ }┃"], &["{ }┃"])?; assert_ctrl_shift_up(&["{ }┃"], &["┃❮{ }"])?;
assert_ctrl_shift_up(&["{ pear┃ }"], &["┃❮{ pear }"])?; assert_ctrl_shift_up(&["{ pear┃ }"], &["┃❮{ pear }"])?;
assert_ctrl_shift_up(&["{ pea┃r }"], &["┃❮{ pear }"])?; assert_ctrl_shift_up(&["{ pea┃r }"], &["┃❮{ pear }"])?;
@ -1340,7 +1552,7 @@ pub mod test_ed_update {
assert_ctrl_shift_up(&["┃{ pear }"], &["┃❮{ pear }"])?; assert_ctrl_shift_up(&["┃{ pear }"], &["┃❮{ pear }"])?;
assert_ctrl_shift_up(&["{ pear ┃}"], &["┃❮{ pear }"])?; assert_ctrl_shift_up(&["{ pear ┃}"], &["┃❮{ pear }"])?;
assert_ctrl_shift_up_repeat(&["{ pear┃ }"], &["┃❮{ pear }"], 3)?; assert_ctrl_shift_up_repeat(&["{ pear┃ }"], &["┃❮{ pear }"], 3)?;
assert_ctrl_shift_up(&["{ pear }┃"], &["{ pear }┃"])?; assert_ctrl_shift_up(&["{ pear }┃"], &["┃❮{ pear }"])?;
assert_ctrl_shift_up(&["{ camelCase123┃ }"], &["┃❮{ camelCase123 }"])?; assert_ctrl_shift_up(&["{ camelCase123┃ }"], &["┃❮{ camelCase123 }"])?;
@ -1349,7 +1561,7 @@ pub mod test_ed_update {
assert_ctrl_shift_up(&["{ a: \"\"┃ }"], &["┃❮{ a: \"\" }"])?; assert_ctrl_shift_up(&["{ a: \"\"┃ }"], &["┃❮{ a: \"\" }"])?;
assert_ctrl_shift_up(&["{ a: \"\" ┃}"], &["┃❮{ a: \"\" }"])?; assert_ctrl_shift_up(&["{ a: \"\" ┃}"], &["┃❮{ a: \"\" }"])?;
assert_ctrl_shift_up_repeat(&["{ a: \"\" ┃}"], &["┃❮{ a: \"\" }"], 3)?; assert_ctrl_shift_up_repeat(&["{ a: \"\" ┃}"], &["┃❮{ a: \"\" }"], 3)?;
assert_ctrl_shift_up(&["{ a: \"\" }┃"], &["{ a: \"\" }┃"])?; assert_ctrl_shift_up(&["{ a: \"\" }┃"], &["┃❮{ a: \"\" }"])?;
assert_ctrl_shift_up(&["{ a:┃ \"\" }"], &["┃❮{ a: \"\" }"])?; assert_ctrl_shift_up(&["{ a:┃ \"\" }"], &["┃❮{ a: \"\" }"])?;
assert_ctrl_shift_up(&["{ a┃: \"\" }"], &["┃❮{ a: \"\" }"])?; assert_ctrl_shift_up(&["{ a┃: \"\" }"], &["┃❮{ a: \"\" }"])?;
assert_ctrl_shift_up(&["{ ┃a: \"\" }"], &["┃❮{ a: \"\" }"])?; assert_ctrl_shift_up(&["{ ┃a: \"\" }"], &["┃❮{ a: \"\" }"])?;
@ -1358,6 +1570,21 @@ pub mod test_ed_update {
assert_ctrl_shift_up_repeat(&["{ a: \"\" }"], &["┃❮{ a: \"\" }"], 2)?; assert_ctrl_shift_up_repeat(&["{ a: \"\" }"], &["┃❮{ a: \"\" }"], 2)?;
assert_ctrl_shift_up_repeat(&["{ a: \"\" }"], &["┃❮{ a: \"\" }"], 4)?; assert_ctrl_shift_up_repeat(&["{ a: \"\" }"], &["┃❮{ a: \"\" }"], 4)?;
assert_ctrl_shift_up(&["{ a: 1┃0 }"], &["{ a: ┃10 }"])?;
assert_ctrl_shift_up(&["{ a: ┃9 }"], &["{ a: ┃9 }"])?;
assert_ctrl_shift_up(&["{ a: 98┃89 }"], &["{ a: ┃9889 }"])?;
assert_ctrl_shift_up(&["{ a: 44┃ }"], &["┃❮{ a: 44 }"])?;
assert_ctrl_shift_up(&["{ a: 0 ┃}"], &["┃❮{ a: 0 }"])?;
assert_ctrl_shift_up_repeat(&["{ a: 123 ┃}"], &["┃❮{ a: 123 }"], 3)?;
assert_ctrl_shift_up(&["{ a: 96 }┃"], &["┃❮{ a: 96 }"])?;
assert_ctrl_shift_up(&["{ a:┃ 985600 }"], &["┃❮{ a: 985600 }"])?;
assert_ctrl_shift_up(&["{ a┃: 5648 }"], &["┃❮{ a: 5648 }"])?;
assert_ctrl_shift_up(&["{ ┃a: 1000000 }"], &["┃❮{ a: 1000000 }"])?;
assert_ctrl_shift_up(&["{┃ a: 1 }"], &["┃❮{ a: 1 }"])?;
assert_ctrl_shift_up(&["┃{ a: 900600 }"], &["┃❮{ a: 900600 }"])?;
assert_ctrl_shift_up_repeat(&["{ a: 10┃000 }"], &["┃❮{ a: 10000 }"], 2)?;
assert_ctrl_shift_up_repeat(&["{ a: ┃45 }"], &["┃❮{ a: 45 }"], 4)?;
assert_ctrl_shift_up(&["{ abc: \"de┃\" }"], &["{ abc: ┃❮\"de\" }"])?; assert_ctrl_shift_up(&["{ abc: \"de┃\" }"], &["{ abc: ┃❮\"de\" }"])?;
assert_ctrl_shift_up(&["{ abc: \"d┃e\" }"], &["{ abc: ┃❮\"de\" }"])?; assert_ctrl_shift_up(&["{ abc: \"d┃e\" }"], &["{ abc: ┃❮\"de\" }"])?;
assert_ctrl_shift_up(&["{ abc: \"┃de\" }"], &["{ abc: ┃❮\"de\" }"])?; assert_ctrl_shift_up(&["{ abc: \"┃de\" }"], &["{ abc: ┃❮\"de\" }"])?;
@ -1432,7 +1659,7 @@ pub mod test_ed_update {
)?; )?;
assert_ctrl_shift_up( assert_ctrl_shift_up(
&["{ abc: { de: \"f g\" } }┃"], &["{ abc: { de: \"f g\" } }┃"],
&["{ abc: { de: \"f g\" } }"], &["┃❮{ abc: { de: \"f g\" } }"],
)?; )?;
assert_ctrl_shift_up_repeat( assert_ctrl_shift_up_repeat(
@ -1451,6 +1678,23 @@ pub mod test_ed_update {
4, 4,
)?; )?;
assert_ctrl_shift_up(&["{ abc: { de: ┃951 } }"], &["{ abc: { de: ┃951 } }"])?;
assert_ctrl_shift_up(&["{ abc: { de: 11┃0 } }"], &["{ abc: { de: ┃110 } }"])?;
assert_ctrl_shift_up(&["{ abc: { de: 444┃ } }"], &["{ abc: ┃❮{ de: 444 } }"])?;
assert_ctrl_shift_up(&["{ abc: { de┃: 99 } }"], &["{ abc: ┃❮{ de: 99 } }"])?;
assert_ctrl_shift_up(&["{ abc: {┃ de: 0 } }"], &["{ abc: ┃❮{ de: 0 } }"])?;
assert_ctrl_shift_up(&["{ abc: { de: 230 ┃} }"], &["{ abc: ┃❮{ de: 230 } }"])?;
assert_ctrl_shift_up(&["{ abc: { de: 7 }┃ }"], &["┃❮{ abc: { de: 7 } }"])?;
assert_ctrl_shift_up(&["┃{ abc: { de: 1 } }"], &["┃❮{ abc: { de: 1 } }"])?;
assert_ctrl_shift_up(
&["{ abc: { de: 111111 } }┃"],
&["┃❮{ abc: { de: 111111 } }"],
)?;
assert_ctrl_shift_up_repeat(&["{ abc: { de: 1┃5 } }"], &["{ abc: ┃❮{ de: 15 } }"], 2)?;
assert_ctrl_shift_up_repeat(&["{ abc: { de: ┃55 } }"], &["┃❮{ abc: { de: 55 } }"], 3)?;
assert_ctrl_shift_up_repeat(&["{ abc: { de: ┃400 } }"], &["┃❮{ abc: { de: 400 } }"], 4)?;
assert_ctrl_shift_up_repeat( assert_ctrl_shift_up_repeat(
&["{ g: { oi: { ng: { d: { e: { e: { p: { camelCase┃ } } } } } } } }"], &["{ g: { oi: { ng: { d: { e: { e: { p: { camelCase┃ } } } } } } } }"],
&["{ g: { oi: { ng: { d: ┃❮{ e: { e: { p: { camelCase } } } } } } } }"], &["{ g: { oi: { ng: { d: ┃❮{ e: { e: { p: { camelCase } } } } } } } }"],
@ -1597,6 +1841,15 @@ pub mod test_ed_update {
Ok(()) Ok(())
} }
#[test]
fn test_ctrl_shift_up_move_int() -> Result<(), String> {
assert_ctrl_shift_single_up_move(&["┃0"], &["0┃"], move_down!())?;
assert_ctrl_shift_single_up_move(&["┃9654"], &["┃9654"], move_up!())?;
assert_ctrl_shift_single_up_move(&["┃100546"], &["100546┃"], move_end!())?;
Ok(())
}
#[test] #[test]
fn test_ctrl_shift_up_move_string() -> Result<(), String> { fn test_ctrl_shift_up_move_string() -> Result<(), String> {
assert_ctrl_shift_single_up_move(&["\"\""], &["\"\""], move_down!())?; assert_ctrl_shift_single_up_move(&["\"\""], &["\"\""], move_down!())?;
@ -1629,6 +1882,13 @@ pub mod test_ed_update {
move_down!(), move_down!(),
)?; )?;
assert_ctrl_shift_up_move(
&["{ camelCase: { cC123: 9┃5 } }"],
&["{ camelCase: { cC123: 95 }┃ }"],
2,
move_down!(),
)?;
Ok(()) Ok(())
} }
@ -1675,11 +1935,21 @@ pub mod test_ed_update {
Ok(()) Ok(())
} }
#[test]
fn test_ctrl_shift_up_backspace_int() -> Result<(), String> {
// Blank is inserted when root is deleted
assert_ctrl_shift_single_up_backspace(&["95┃21"], &[""])?;
assert_ctrl_shift_single_up_backspace(&["0┃"], &[""])?;
assert_ctrl_shift_single_up_backspace(&["┃10000"], &[""])?;
Ok(())
}
#[test] #[test]
fn test_ctrl_shift_up_backspace_string() -> Result<(), String> { fn test_ctrl_shift_up_backspace_string() -> Result<(), String> {
// Blank is inserted when root is deleted // Blank is inserted when root is deleted
assert_ctrl_shift_single_up_backspace(&["\"\""], &[""])?; assert_ctrl_shift_single_up_backspace(&["\"\""], &[""])?;
assert_ctrl_shift_single_up_backspace(&["\"\""], &["\"\""])?; assert_ctrl_shift_single_up_backspace(&["\"\""], &[" "])?;
assert_ctrl_shift_single_up_backspace(&["\"abc\""], &[""])?; assert_ctrl_shift_single_up_backspace(&["\"abc\""], &[""])?;
assert_ctrl_shift_single_up_backspace( assert_ctrl_shift_single_up_backspace(
&["\"hello┃, hello.0123456789ZXY{}[]-><-\""], &["\"hello┃, hello.0123456789ZXY{}[]-><-\""],
@ -1699,6 +1969,7 @@ pub mod test_ed_update {
assert_ctrl_shift_single_up_backspace(&["{ a: ┃{ b } }"], &["{ a: ┃ }"])?; assert_ctrl_shift_single_up_backspace(&["{ a: ┃{ b } }"], &["{ a: ┃ }"])?;
assert_ctrl_shift_single_up_backspace(&["{ a: \"┃b cd\" }"], &["{ a: ┃ }"])?; assert_ctrl_shift_single_up_backspace(&["{ a: \"┃b cd\" }"], &["{ a: ┃ }"])?;
assert_ctrl_shift_single_up_backspace(&["{ a: ┃12 }"], &["{ a: ┃ }"])?;
assert_ctrl_shift_single_up_backspace( assert_ctrl_shift_single_up_backspace(
&["{ g: { oi: { ng: { d: { ┃e: { e: { p: { camelCase } } } } } } } }"], &["{ g: { oi: { ng: { d: { ┃e: { e: { p: { camelCase } } } } } } } }"],
&["{ g: { oi: { ng: { d: ┃ } } } }"], &["{ g: { oi: { ng: { d: ┃ } } } }"],
@ -1709,6 +1980,11 @@ pub mod test_ed_update {
&["{ a: { b: ┃ } }"], &["{ a: { b: ┃ } }"],
2, 2,
)?; )?;
assert_ctrl_shift_up_backspace(
&["{ a: { b: { c: 100┃000 } } }"],
&["{ a: { b: ┃ } }"],
2,
)?;
assert_ctrl_shift_up_backspace(&["{ a: { b: { c: {┃ } } } }"], &["{ a: { b: ┃ } }"], 2)?; assert_ctrl_shift_up_backspace(&["{ a: { b: { c: {┃ } } } }"], &["{ a: { b: ┃ } }"], 2)?;
assert_ctrl_shift_up_backspace( assert_ctrl_shift_up_backspace(
&["{ g: { oi: { ng: { d: { e: { e: { p┃: { camelCase } } } } } } } }"], &["{ g: { oi: { ng: { d: { e: { e: { p┃: { camelCase } } } } } } } }"],

View file

@ -0,0 +1,156 @@
use crate::editor::ed_error::EdResult;
use crate::editor::ed_error::StringParseError;
use crate::editor::markup::attribute::Attributes;
use crate::editor::markup::nodes::MarkupNode;
use crate::editor::mvc::app_update::InputOutcome;
use crate::editor::mvc::ed_model::EdModel;
use crate::editor::mvc::ed_update::get_node_context;
use crate::editor::mvc::ed_update::NodeContext;
use crate::editor::slow_pool::MarkNodeId;
use crate::editor::syntax_highlight::HighlightStyle;
use crate::lang::ast::Expr2::SmallInt;
use crate::lang::ast::IntVal;
use crate::lang::ast::{IntStyle, IntVal::*};
use crate::lang::pool::PoolStr;
use crate::ui::text::lines::SelectableLines;
// digit_char should be verified to be a digit before calling this function
pub fn start_new_int(ed_model: &mut EdModel, digit_char: &char) -> EdResult<InputOutcome> {
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 int_var = ed_model.module.env.var_store.fresh();
let digit_string = digit_char.to_string();
let expr2_node = SmallInt {
number: IntVal::U64(*digit_char as u64), // TODO determine if u64 on wordlength of current arch, perhaps introduce Unknown(i64)
var: int_var,
style: IntStyle::Decimal,
text: PoolStr::new(&digit_string, &mut ed_model.module.env.pool),
};
ed_model.module.env.pool.set(ast_node_id, expr2_node);
let int_node = MarkupNode::Text {
content: digit_string,
ast_node_id,
syn_high_style: HighlightStyle::Number,
attributes: Attributes::new(),
parent_id_opt,
};
if is_blank_node {
ed_model
.markup_node_pool
.replace_node(curr_mark_node_id, int_node);
// remove data corresponding to Blank node
ed_model.del_at_line(old_caret_pos.line, old_caret_pos.column)?;
let char_len = 1;
ed_model.simple_move_carets_right(char_len);
// update GridNodeMap and CodeLines
ed_model.insert_between_line(
old_caret_pos.line,
old_caret_pos.column,
&digit_char.to_string(),
curr_mark_node_id,
)?;
Ok(InputOutcome::Accepted)
} else {
Ok(InputOutcome::Ignored)
}
}
// TODO check if new int needs more than e.g. 64 bits
pub fn update_int(
ed_model: &mut EdModel,
int_mark_node_id: MarkNodeId,
ch: &char,
) -> EdResult<InputOutcome> {
if ch.is_ascii_digit() {
let old_caret_pos = ed_model.get_caret();
let node_caret_offset = ed_model
.grid_node_map
.get_offset_to_node_id(old_caret_pos, int_mark_node_id)?;
let int_mark_node = ed_model.markup_node_pool.get_mut(int_mark_node_id);
let int_ast_node_id = int_mark_node.get_ast_node_id();
let content_str_mut = int_mark_node.get_content_mut()?;
// 00, 01 are not valid ints
if (content_str_mut == "0" && (node_caret_offset == 1 || *ch == '0'))
|| (*ch == '0' && node_caret_offset == 0)
{
Ok(InputOutcome::Ignored)
} else {
content_str_mut.insert(node_caret_offset, *ch);
let content_str = int_mark_node.get_content()?;
// update GridNodeMap and CodeLines
ed_model.insert_between_line(
old_caret_pos.line,
old_caret_pos.column,
&ch.to_string(),
int_mark_node_id,
)?;
// update ast
let new_pool_str = PoolStr::new(&content_str, ed_model.module.env.pool);
let int_ast_node = ed_model.module.env.pool.get_mut(int_ast_node_id);
match int_ast_node {
SmallInt { number, text, .. } => {
update_small_int_num(number, &content_str)?;
*text = new_pool_str;
}
_ => unimplemented!("TODO implement updating this type of Number"),
}
// update caret
ed_model.simple_move_carets_right(1);
Ok(InputOutcome::Accepted)
}
} else {
Ok(InputOutcome::Ignored)
}
}
fn update_small_int_num(number: &mut IntVal, updated_str: &str) -> EdResult<()> {
*number = match number {
I64(_) => I64(check_parse_res(updated_str.parse::<i64>())?),
U64(_) => U64(check_parse_res(updated_str.parse::<u64>())?),
I32(_) => I32(check_parse_res(updated_str.parse::<i32>())?),
U32(_) => U32(check_parse_res(updated_str.parse::<u32>())?),
I16(_) => I16(check_parse_res(updated_str.parse::<i16>())?),
U16(_) => U16(check_parse_res(updated_str.parse::<u16>())?),
I8(_) => I8(check_parse_res(updated_str.parse::<i8>())?),
U8(_) => U8(check_parse_res(updated_str.parse::<u8>())?),
};
Ok(())
}
fn check_parse_res<T, E: std::fmt::Debug>(parse_res: Result<T, E>) -> EdResult<T> {
match parse_res {
Ok(some_type) => Ok(some_type),
Err(parse_err) => StringParseError {
msg: format!("{:?}", parse_err),
}
.fail(),
}
}

View file

@ -3,6 +3,7 @@ pub mod app_update;
pub mod ed_model; pub mod ed_model;
pub mod ed_update; pub mod ed_update;
pub mod ed_view; pub mod ed_view;
mod int_update;
mod lookup_update; mod lookup_update;
mod record_update; mod record_update;
mod string_update; mod string_update;

View file

@ -96,69 +96,77 @@ pub fn update_empty_record(
sibling_ids: Vec<MarkNodeId>, sibling_ids: Vec<MarkNodeId>,
ed_model: &mut EdModel, ed_model: &mut EdModel,
) -> EdResult<InputOutcome> { ) -> EdResult<InputOutcome> {
let prev_mark_node = ed_model.markup_node_pool.get(prev_mark_node_id); let mut input_chars = new_input.chars();
let NodeContext { if input_chars.all(|ch| ch.is_ascii_alphabetic())
old_caret_pos, && input_chars.all(|ch| ch.is_ascii_lowercase())
curr_mark_node_id,
curr_mark_node,
parent_id_opt,
ast_node_id,
} = get_node_context(&ed_model)?;
if prev_mark_node.get_content()? == nodes::LEFT_ACCOLADE
&& curr_mark_node.get_content()? == nodes::RIGHT_ACCOLADE
{ {
// update AST let prev_mark_node = ed_model.markup_node_pool.get(prev_mark_node_id);
let record_var = ed_model.module.env.var_store.fresh();
let field_name = PoolStr::new(new_input, &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 first_field = RecordField::InvalidLabelOnly(field_name, field_var);
let fields = PoolVec::new(vec![first_field].into_iter(), &mut ed_model.module.env.pool); let NodeContext {
old_caret_pos,
let new_ast_node = Expr2::Record { record_var, fields }; curr_mark_node_id,
curr_mark_node,
ed_model.module.env.pool.set(ast_node_id, new_ast_node);
// update Markup
let record_field_node = MarkupNode::Text {
content: new_input.to_owned(),
ast_node_id,
syn_high_style: HighlightStyle::RecordField,
attributes: Attributes::new(),
parent_id_opt, parent_id_opt,
}; ast_node_id,
} = get_node_context(&ed_model)?;
let record_field_node_id = ed_model.markup_node_pool.add(record_field_node); if prev_mark_node.get_content()? == nodes::LEFT_ACCOLADE
&& curr_mark_node.get_content()? == nodes::RIGHT_ACCOLADE
{
// update AST
let record_var = ed_model.module.env.var_store.fresh();
let field_name = PoolStr::new(new_input, &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 first_field = RecordField::InvalidLabelOnly(field_name, field_var);
if let Some(parent_id) = parent_id_opt { let fields = PoolVec::new(vec![first_field].into_iter(), &mut ed_model.module.env.pool);
let parent = ed_model.markup_node_pool.get_mut(parent_id);
let new_child_index = index_of(curr_mark_node_id, &sibling_ids)?; let new_ast_node = Expr2::Record { record_var, fields };
parent.add_child_at_index(new_child_index, record_field_node_id)?; ed_model.module.env.pool.set(ast_node_id, new_ast_node);
} else {
MissingParent { // update Markup
node_id: curr_mark_node_id,
let record_field_node = MarkupNode::Text {
content: new_input.to_owned(),
ast_node_id,
syn_high_style: HighlightStyle::RecordField,
attributes: Attributes::new(),
parent_id_opt,
};
let record_field_node_id = ed_model.markup_node_pool.add(record_field_node);
if let Some(parent_id) = parent_id_opt {
let parent = ed_model.markup_node_pool.get_mut(parent_id);
let new_child_index = index_of(curr_mark_node_id, &sibling_ids)?;
parent.add_child_at_index(new_child_index, record_field_node_id)?;
} else {
MissingParent {
node_id: curr_mark_node_id,
}
.fail()?
} }
.fail()?
// update caret
ed_model.simple_move_carets_right(1);
// update GridNodeMap and CodeLines
ed_model.insert_between_line(
old_caret_pos.line,
old_caret_pos.column,
new_input,
record_field_node_id,
)?;
Ok(InputOutcome::Accepted)
} else {
Ok(InputOutcome::Ignored)
} }
// update caret
ed_model.simple_move_carets_right(1);
// update GridNodeMap and CodeLines
ed_model.insert_between_line(
old_caret_pos.line,
old_caret_pos.column,
new_input,
record_field_node_id,
)?;
Ok(InputOutcome::Accepted)
} else { } else {
Ok(InputOutcome::Ignored) Ok(InputOutcome::Ignored)
} }
@ -173,87 +181,124 @@ pub fn update_record_colon(
curr_mark_node_id, curr_mark_node_id,
curr_mark_node, curr_mark_node,
parent_id_opt, parent_id_opt,
ast_node_id: _, ast_node_id,
} = get_node_context(&ed_model)?; } = get_node_context(&ed_model)?;
if let Some(parent_id) = parent_id_opt { if let Some(parent_id) = parent_id_opt {
let sibling_ids = curr_mark_node.get_sibling_ids(&ed_model.markup_node_pool); let curr_ast_node = ed_model.module.env.pool.get(ast_node_id);
let new_child_index = index_of(curr_mark_node_id, &sibling_ids)?; let prev_mark_node_id_opt = ed_model.get_prev_mark_node_id()?;
if let Some(prev_mark_node_id) = prev_mark_node_id_opt {
let prev_mark_node = ed_model.markup_node_pool.get(prev_mark_node_id);
let ast_node_ref = ed_model.module.env.pool.get(record_ast_node_id); let prev_ast_node = ed_model
.module
.env
.pool
.get(prev_mark_node.get_ast_node_id());
match ast_node_ref { // current and prev node should always point to record when in valid position to add ':'
Expr2::Record { if matches!(prev_ast_node, Expr2::Record { .. })
record_var: _, && matches!(curr_ast_node, Expr2::Record { .. })
fields, {
} => { let sibling_ids = curr_mark_node.get_sibling_ids(&ed_model.markup_node_pool);
// 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 let new_child_index = index_of(curr_mark_node_id, &sibling_ids)?;
.iter_mut(ed_model.module.env.pool)
.next()
.with_context(|| RecordWithoutFields {})?;
*first_field_mut = RecordField::LabeledValue( let ast_node_ref = ed_model.module.env.pool.get(record_ast_node_id);
*first_field_mut.get_record_field_pool_str(),
*first_field_mut.get_record_field_var(),
new_field_val_id,
);
// update Markup match ast_node_ref {
let record_colon = nodes::COLON; Expr2::Record {
record_var: _,
fields,
} => {
if ed_model.node_exists_at_caret() {
let next_mark_node_id =
ed_model.grid_node_map.get_id_at_row_col(old_caret_pos)?;
let next_mark_node = ed_model.markup_node_pool.get(next_mark_node_id);
if next_mark_node.get_content()? == nodes::RIGHT_ACCOLADE {
// update AST node
let new_field_val = Expr2::Blank;
let new_field_val_id = ed_model.module.env.pool.add(new_field_val);
let record_colon_node = MarkupNode::Text { let first_field_mut = fields
content: record_colon.to_owned(), .iter_mut(ed_model.module.env.pool)
ast_node_id: record_ast_node_id, .next()
syn_high_style: HighlightStyle::Operator, .with_context(|| RecordWithoutFields {})?;
attributes: Attributes::new(),
parent_id_opt: Some(parent_id),
};
let record_colon_node_id = ed_model.markup_node_pool.add(record_colon_node); *first_field_mut = RecordField::LabeledValue(
ed_model *first_field_mut.get_record_field_pool_str(),
.markup_node_pool *first_field_mut.get_record_field_var(),
.get_mut(parent_id) new_field_val_id,
.add_child_at_index(new_child_index, record_colon_node_id)?; );
let record_blank_node = MarkupNode::Blank { // update Markup
ast_node_id: new_field_val_id, let record_colon = nodes::COLON;
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); let record_colon_node = MarkupNode::Text {
ed_model content: record_colon.to_owned(),
.markup_node_pool ast_node_id: record_ast_node_id,
.get_mut(parent_id) syn_high_style: HighlightStyle::Operator,
.add_child_at_index(new_child_index + 1, record_blank_node_id)?; attributes: Attributes::new(),
parent_id_opt: Some(parent_id),
};
// update caret let record_colon_node_id =
ed_model.simple_move_carets_right(record_colon.len()); 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)?;
// update GridNodeMap and CodeLines let record_blank_node = MarkupNode::Blank {
ed_model.insert_between_line( ast_node_id: new_field_val_id,
old_caret_pos.line, syn_high_style: HighlightStyle::Blank,
old_caret_pos.column, attributes: Attributes::new(),
nodes::COLON, parent_id_opt: Some(parent_id),
record_colon_node_id, };
)?;
ed_model.insert_between_line( let record_blank_node_id =
old_caret_pos.line, ed_model.markup_node_pool.add(record_blank_node);
old_caret_pos.column + nodes::COLON.len(), ed_model
nodes::BLANK_PLACEHOLDER, .markup_node_pool
record_blank_node_id, .get_mut(parent_id)
)?; .add_child_at_index(
new_child_index + 1,
record_blank_node_id,
)?;
Ok(InputOutcome::Accepted) // update caret
ed_model.simple_move_carets_right(record_colon.len());
// update GridNodeMap and CodeLines
ed_model.insert_between_line(
old_caret_pos.line,
old_caret_pos.column,
nodes::COLON,
record_colon_node_id,
)?;
ed_model.insert_between_line(
old_caret_pos.line,
old_caret_pos.column + nodes::COLON.len(),
nodes::BLANK_PLACEHOLDER,
record_blank_node_id,
)?;
Ok(InputOutcome::Accepted)
} else {
Ok(InputOutcome::Ignored)
}
} else {
Ok(InputOutcome::Ignored)
}
}
_ => Ok(InputOutcome::Ignored),
}
} else {
Ok(InputOutcome::Ignored)
} }
other => unimplemented!("TODO implement updating of Expr2 {:?}.", other), } else {
Ok(InputOutcome::Ignored)
} }
} else { } else {
MissingParent { MissingParent {

View file

@ -1,3 +1,3 @@
pub const NOTHING_OPENED: &str = "Execute `cargo run edit <filename>` to open a file."; pub const NOTHING_OPENED: &str = "Execute `cargo run edit <filename>` to open a file.";
pub const START_TIP: &str = pub const START_TIP: &str =
"Start by typing '{' or '\"'.\nInput chars that would create parse errors will be ignored."; "Start by typing '{', '\"' or a number.\nInput chars that would create parse errors will be ignored.";

View file

@ -437,6 +437,9 @@ fn expr2_to_string_helper(
Expr2::InvalidLookup(pool_str) => { Expr2::InvalidLookup(pool_str) => {
out_string.push_str(&format!("InvalidLookup({})", pool_str.as_str(pool))); out_string.push_str(&format!("InvalidLookup({})", pool_str.as_str(pool)));
} }
Expr2::SmallInt { text, .. } => {
out_string.push_str(&format!("SmallInt({})", text.as_str(pool)));
}
other => todo!("Implement for {:?}", other), other => todo!("Implement for {:?}", other),
} }

View file

@ -1,34 +1,91 @@
use crate::lang::pool::{Pool, PoolVec}; use bumpalo::{collections::Vec as BumpVec, Bump};
use crate::lang::{ast::Expr2, expr::Env, types::Type2};
use crate::lang::pool::{Pool, PoolStr, PoolVec, ShallowClone};
use crate::lang::{
ast::Expr2,
expr::Env,
types::{Type2, TypeId},
};
use roc_can::expected::Expected; use roc_can::expected::Expected;
use roc_collections::all::SendMap;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_region::all::Region; use roc_region::all::{Located, Region};
use roc_types::types::Category; use roc_types::{
subs::Variable,
types::{Category, Reason},
};
#[derive(Debug)] #[derive(Debug)]
pub enum Constraint { pub enum Constraint<'a> {
Eq(Type2, Expected<Type2>, Category, Region), Eq(Type2, Expected<Type2>, Category, Region),
// Store(Type, Variable, &'static str, u32), // Store(Type, Variable, &'static str, u32),
// Lookup(Symbol, Expected<Type>, Region), // Lookup(Symbol, Expected<Type>, Region),
// Pattern(Region, PatternCategory, Type, PExpected<Type>), // Pattern(Region, PatternCategory, Type, PExpected<Type>),
And(BumpVec<'a, Constraint<'a>>),
Let(&'a LetConstraint<'a>),
// SaveTheEnvironment,
True, // Used for things that always unify, e.g. blanks and runtime errors True, // Used for things that always unify, e.g. blanks and runtime errors
// SaveTheEnvironment,
// Let(Box<LetConstraint>),
// And(Vec<Constraint>),
} }
pub fn constrain_expr( #[derive(Debug)]
pub struct LetConstraint<'a> {
pub rigid_vars: BumpVec<'a, Variable>,
pub flex_vars: BumpVec<'a, Variable>,
pub def_types: SendMap<Symbol, Located<Type2>>,
pub defs_constraint: Constraint<'a>,
pub ret_constraint: Constraint<'a>,
}
pub fn constrain_expr<'a>(
arena: &'a Bump,
env: &mut Env, env: &mut Env,
expr: &Expr2, expr: &Expr2,
expected: Expected<Type2>, expected: Expected<Type2>,
region: Region, region: Region,
) -> Constraint { ) -> Constraint<'a> {
use Constraint::*; use Constraint::*;
match expr { match expr {
Expr2::EmptyRecord => Eq(Type2::EmptyRec, expected, Category::Record, region),
Expr2::Str(_) => Eq(str_type(env.pool), expected, Category::Str, region), Expr2::Str(_) => Eq(str_type(env.pool), expected, Category::Str, region),
Expr2::EmptyRecord => Eq(Type2::EmptyRec, expected, Category::Record, region),
Expr2::SmallInt { var, .. } => {
let mut flex_vars = BumpVec::with_capacity_in(1, arena);
let rigid_vars = BumpVec::new_in(arena);
let mut and_constraints = BumpVec::with_capacity_in(2, arena);
let num_type = Type2::Variable(*var);
flex_vars.push(*var);
let precision_var = env.var_store.fresh();
let range_type = Type2::Variable(precision_var);
let range_type_id = env.pool.add(range_type);
and_constraints.push(Eq(
num_type.shallow_clone(),
Expected::ForReason(Reason::IntLiteral, num_int(env.pool, range_type_id), region),
Category::Int,
region,
));
and_constraints.push(Eq(num_type, expected, Category::Int, region));
let defs_constraint = And(and_constraints);
let let_constraint = arena.alloc(LetConstraint {
rigid_vars,
flex_vars,
def_types: SendMap::default(),
defs_constraint,
ret_constraint: Constraint::True,
});
Let(let_constraint)
}
_ => todo!("implement constaints for {:?}", expr), _ => todo!("implement constaints for {:?}", expr),
} }
} }
@ -36,3 +93,83 @@ pub fn constrain_expr(
fn str_type(pool: &mut Pool) -> Type2 { fn str_type(pool: &mut Pool) -> Type2 {
Type2::Apply(Symbol::STR_STR, PoolVec::empty(pool)) Type2::Apply(Symbol::STR_STR, PoolVec::empty(pool))
} }
fn num_int(pool: &mut Pool, range: TypeId) -> Type2 {
let num_integer_type = num_integer(pool, range);
let num_integer_id = pool.add(num_integer_type);
let num_num_type = num_num(pool, num_integer_id);
let num_num_id = pool.add(num_num_type);
Type2::Alias(
Symbol::NUM_INT,
PoolVec::new(vec![(PoolStr::new("range", pool), range)].into_iter(), pool),
num_num_id,
)
}
fn _num_signed64(pool: &mut Pool) -> Type2 {
let alias_content = Type2::TagUnion(
PoolVec::new(
// TagName::Private(Symbol::NUM_AT_SIGNED64)
vec![(PoolStr::new("Num.@Signed64", pool), PoolVec::empty(pool))].into_iter(),
pool,
),
pool.add(Type2::EmptyTagUnion),
);
Type2::Alias(
Symbol::NUM_SIGNED64,
PoolVec::empty(pool),
pool.add(alias_content),
)
}
fn num_integer(pool: &mut Pool, range: TypeId) -> Type2 {
let range_type = pool.get(range);
let alias_content = Type2::TagUnion(
PoolVec::new(
vec![(
// TagName::Private(Symbol::NUM_AT_INTEGER)
PoolStr::new("Num.@Integer", pool),
PoolVec::new(vec![range_type.shallow_clone()].into_iter(), pool),
)]
.into_iter(),
pool,
),
pool.add(Type2::EmptyTagUnion),
);
Type2::Alias(
Symbol::NUM_INTEGER,
PoolVec::new(vec![(PoolStr::new("range", pool), range)].into_iter(), pool),
pool.add(alias_content),
)
}
fn num_num(pool: &mut Pool, type_id: TypeId) -> Type2 {
let range_type = pool.get(type_id);
let alias_content = Type2::TagUnion(
PoolVec::new(
vec![(
// TagName::Private(Symbol::NUM_AT_NUM)
PoolStr::new("Num.@Num", pool),
PoolVec::new(vec![range_type.shallow_clone()].into_iter(), pool),
)]
.into_iter(),
pool,
),
pool.add(Type2::EmptyTagUnion),
);
Type2::Alias(
Symbol::NUM_NUM,
PoolVec::new(
vec![(PoolStr::new("range", pool), type_id)].into_iter(),
pool,
),
pool.add(alias_content),
)
}

View file

@ -4,7 +4,7 @@ use crate::lang::constrain::Constraint::{self, *};
use crate::lang::pool::Pool; use crate::lang::pool::Pool;
use crate::lang::types::Type2; use crate::lang::types::Type2;
use roc_can::expected::{Expected, PExpected}; use roc_can::expected::{Expected, PExpected};
use roc_collections::all::MutMap; use roc_collections::all::{ImMap, MutMap};
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_region::all::{Located, Region}; use roc_region::all::{Located, Region};
use roc_types::solved_types::Solved; use roc_types::solved_types::Solved;
@ -177,7 +177,7 @@ pub fn run_in_place(
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn solve( fn solve(
mempool: &mut Pool, mempool: &mut Pool,
_env: &Env, env: &Env,
state: State, state: State,
rank: Rank, rank: Rank,
pools: &mut Pools, pools: &mut Pools,
@ -235,387 +235,407 @@ fn solve(
state state
} }
} }
} // Store(source, target, _filename, _linenr) => { }
// // a special version of Eq that is used to store types in the AST. // Store(source, target, _filename, _linenr) => {
// // IT DOES NOT REPORT ERRORS! // // a special version of Eq that is used to store types in the AST.
// let actual = type_to_var(subs, rank, pools, cached_aliases, source); // // IT DOES NOT REPORT ERRORS!
// let target = *target; // let actual = type_to_var(subs, rank, pools, cached_aliases, source);
// // let target = *target;
// match unify(subs, actual, target) { //
// Success(vars) => { // match unify(subs, actual, target) {
// introduce(subs, rank, pools, &vars); // Success(vars) => {
// // introduce(subs, rank, pools, &vars);
// state //
// } // state
// Failure(vars, _actual_type, _expected_type) => { // }
// introduce(subs, rank, pools, &vars); // Failure(vars, _actual_type, _expected_type) => {
// // introduce(subs, rank, pools, &vars);
// // ERROR NOT REPORTED //
// // // ERROR NOT REPORTED
// state //
// } // state
// BadType(vars, _problem) => { // }
// introduce(subs, rank, pools, &vars); // BadType(vars, _problem) => {
// // introduce(subs, rank, pools, &vars);
// // ERROR NOT REPORTED //
// // // ERROR NOT REPORTED
// state //
// } // state
// } // }
// } // }
// Lookup(symbol, expectation, region) => { // }
// match env.vars_by_symbol.get(&symbol) { // Lookup(symbol, expectation, region) => {
// Some(var) => { // match env.vars_by_symbol.get(&symbol) {
// // Deep copy the vars associated with this symbol before unifying them. // Some(var) => {
// // Otherwise, suppose we have this: // // Deep copy the vars associated with this symbol before unifying them.
// // // // Otherwise, suppose we have this:
// // identity = \a -> a // //
// // // // identity = \a -> a
// // x = identity 5 // //
// // // // x = identity 5
// // When we call (identity 5), it's important that we not unify // //
// // on identity's original vars. If we do, the type of `identity` will be // // When we call (identity 5), it's important that we not unify
// // mutated to be `Int -> Int` instead of `a -> `, which would be incorrect; // // on identity's original vars. If we do, the type of `identity` will be
// // the type of `identity` is more general than that! // // mutated to be `Int -> Int` instead of `a -> `, which would be incorrect;
// // // // the type of `identity` is more general than that!
// // Instead, we want to unify on a *copy* of its vars. If the copy unifies // //
// // successfully (in this case, to `Int -> Int`), we can use that to // // Instead, we want to unify on a *copy* of its vars. If the copy unifies
// // infer the type of this lookup (in this case, `Int`) without ever // // successfully (in this case, to `Int -> Int`), we can use that to
// // having mutated the original. // // infer the type of this lookup (in this case, `Int`) without ever
// // // // having mutated the original.
// // If this Lookup is targeting a value in another module, // //
// // then we copy from that module's Subs into our own. If the value // // If this Lookup is targeting a value in another module,
// // is being looked up in this module, then we use our Subs as both // // then we copy from that module's Subs into our own. If the value
// // the source and destination. // // is being looked up in this module, then we use our Subs as both
// let actual = deep_copy_var(subs, rank, pools, *var); // // the source and destination.
// let expected = type_to_var( // let actual = deep_copy_var(subs, rank, pools, *var);
// subs, // let expected = type_to_var(
// rank, // subs,
// pools, // rank,
// cached_aliases, // pools,
// expectation.get_type_ref(), // cached_aliases,
// ); // expectation.get_type_ref(),
// match unify(subs, actual, expected) { // );
// Success(vars) => { // match unify(subs, actual, expected) {
// introduce(subs, rank, pools, &vars); // Success(vars) => {
// // introduce(subs, rank, pools, &vars);
// state //
// } // state
// // }
// Failure(vars, actual_type, expected_type) => { //
// introduce(subs, rank, pools, &vars); // Failure(vars, actual_type, expected_type) => {
// // introduce(subs, rank, pools, &vars);
// let problem = TypeError::BadExpr( //
// *region, // let problem = TypeError::BadExpr(
// Category::Lookup(*symbol), // *region,
// actual_type, // Category::Lookup(*symbol),
// expectation.clone().replace(expected_type), // actual_type,
// ); // expectation.clone().replace(expected_type),
// // );
// problems.push(problem); //
// // problems.push(problem);
// state //
// } // state
// BadType(vars, problem) => { // }
// introduce(subs, rank, pools, &vars); // BadType(vars, problem) => {
// // introduce(subs, rank, pools, &vars);
// problems.push(TypeError::BadType(problem)); //
// // problems.push(TypeError::BadType(problem));
// state //
// } // state
// } // }
// } // }
// None => { // }
// problems.push(TypeError::UnexposedLookup(*symbol)); // None => {
// // problems.push(TypeError::UnexposedLookup(*symbol));
// state //
// } // state
// } // }
// } // }
// And(sub_constraints) => { // }
// let mut state = state; And(sub_constraints) => {
// let mut state = state;
// for sub_constraint in sub_constraints.iter() {
// state = solve( for sub_constraint in sub_constraints.iter() {
// env, state = solve(
// state, mempool,
// rank, env,
// pools, state,
// problems, rank,
// cached_aliases, pools,
// subs, problems,
// sub_constraint, cached_aliases,
// ); subs,
// } sub_constraint,
// );
// state }
// }
// Pattern(region, category, typ, expectation) => { state
// let actual = type_to_var(subs, rank, pools, cached_aliases, typ); }
// let expected = type_to_var( // Pattern(region, category, typ, expectation) => {
// subs, // let actual = type_to_var(subs, rank, pools, cached_aliases, typ);
// rank, // let expected = type_to_var(
// pools, // subs,
// cached_aliases, // rank,
// expectation.get_type_ref(), // pools,
// ); // cached_aliases,
// // expectation.get_type_ref(),
// match unify(subs, actual, expected) { // );
// Success(vars) => { //
// introduce(subs, rank, pools, &vars); // match unify(subs, actual, expected) {
// // Success(vars) => {
// state // introduce(subs, rank, pools, &vars);
// } //
// Failure(vars, actual_type, expected_type) => { // state
// introduce(subs, rank, pools, &vars); // }
// // Failure(vars, actual_type, expected_type) => {
// let problem = TypeError::BadPattern( // introduce(subs, rank, pools, &vars);
// *region, //
// category.clone(), // let problem = TypeError::BadPattern(
// actual_type, // *region,
// expectation.clone().replace(expected_type), // category.clone(),
// ); // actual_type,
// // expectation.clone().replace(expected_type),
// problems.push(problem); // );
// //
// state // problems.push(problem);
// } //
// BadType(vars, problem) => { // state
// introduce(subs, rank, pools, &vars); // }
// // BadType(vars, problem) => {
// problems.push(TypeError::BadType(problem)); // introduce(subs, rank, pools, &vars);
// //
// state // problems.push(TypeError::BadType(problem));
// } //
// } // state
// } // }
// Let(let_con) => { // }
// match &let_con.ret_constraint { // }
// True if let_con.rigid_vars.is_empty() => { Let(let_con) => {
// introduce(subs, rank, pools, &let_con.flex_vars); match &let_con.ret_constraint {
// True if let_con.rigid_vars.is_empty() => {
// // If the return expression is guaranteed to solve, introduce(subs, rank, pools, &let_con.flex_vars);
// // solve the assignments themselves and move on.
// solve( // If the return expression is guaranteed to solve,
// &env, // solve the assignments themselves and move on.
// state, solve(
// rank, mempool,
// pools, &env,
// problems, state,
// cached_aliases, rank,
// subs, pools,
// &let_con.defs_constraint, problems,
// ) cached_aliases,
// } subs,
// ret_con if let_con.rigid_vars.is_empty() && let_con.flex_vars.is_empty() => { &let_con.defs_constraint,
// let state = solve( )
// env, }
// state, ret_con if let_con.rigid_vars.is_empty() && let_con.flex_vars.is_empty() => {
// rank, let state = solve(
// pools, mempool,
// problems, env,
// cached_aliases, state,
// subs, rank,
// &let_con.defs_constraint, pools,
// ); problems,
// cached_aliases,
// // Add a variable for each def to new_vars_by_env. subs,
// let mut local_def_vars = ImMap::default(); &let_con.defs_constraint,
// );
// for (symbol, loc_type) in let_con.def_types.iter() {
// let var = type_to_var(subs, rank, pools, cached_aliases, &loc_type.value); // Add a variable for each def to new_vars_by_env.
// let mut local_def_vars = ImMap::default();
// local_def_vars.insert(
// *symbol, for (symbol, loc_type) in let_con.def_types.iter() {
// Located { let var = type_to_var(
// value: var, mempool,
// region: loc_type.region, subs,
// }, rank,
// ); pools,
// } cached_aliases,
// &loc_type.value,
// let mut new_env = env.clone(); );
// for (symbol, loc_var) in local_def_vars.iter() {
// if !new_env.vars_by_symbol.contains_key(&symbol) { local_def_vars.insert(
// new_env.vars_by_symbol.insert(*symbol, loc_var.value); *symbol,
// } Located {
// } value: var,
// region: loc_type.region,
// let new_state = solve( },
// &new_env, );
// state, }
// rank,
// pools, let mut new_env = env.clone();
// problems, for (symbol, loc_var) in local_def_vars.iter() {
// cached_aliases, if !new_env.vars_by_symbol.contains_key(&symbol) {
// subs, new_env.vars_by_symbol.insert(*symbol, loc_var.value);
// ret_con, }
// ); }
//
// for (symbol, loc_var) in local_def_vars { let new_state = solve(
// check_for_infinite_type(subs, problems, symbol, loc_var); mempool,
// } &new_env,
// state,
// new_state rank,
// } pools,
// ret_con => { problems,
// let rigid_vars = &let_con.rigid_vars; cached_aliases,
// let flex_vars = &let_con.flex_vars; subs,
// ret_con,
// // work in the next pool to localize header );
// let next_rank = rank.next();
// for (symbol, loc_var) in local_def_vars {
// // introduce variables check_for_infinite_type(subs, problems, symbol, loc_var);
// for &var in rigid_vars.iter().chain(flex_vars.iter()) { }
// subs.set_rank(var, next_rank);
// } new_state
// }
// // determine the next pool ret_con => {
// let next_pools; let rigid_vars = &let_con.rigid_vars;
// if next_rank.into_usize() < pools.len() { let flex_vars = &let_con.flex_vars;
// next_pools = pools
// } else { // work in the next pool to localize header
// // we should be off by one at this point let next_rank = rank.next();
// debug_assert_eq!(next_rank.into_usize(), 1 + pools.len());
// pools.extend_to(next_rank.into_usize()); // introduce variables
// next_pools = pools; for &var in rigid_vars.iter().chain(flex_vars.iter()) {
// } subs.set_rank(var, next_rank);
// }
// let pool: &mut Vec<Variable> = next_pools.get_mut(next_rank);
// // determine the next pool
// // Replace the contents of this pool with rigid_vars and flex_vars let next_pools;
// pool.clear(); if next_rank.into_usize() < pools.len() {
// pool.reserve(rigid_vars.len() + flex_vars.len()); next_pools = pools
// pool.extend(rigid_vars.iter()); } else {
// pool.extend(flex_vars.iter()); // we should be off by one at this point
// debug_assert_eq!(next_rank.into_usize(), 1 + pools.len());
// // run solver in next pool pools.extend_to(next_rank.into_usize());
// next_pools = pools;
// // Add a variable for each def to local_def_vars. }
// let mut local_def_vars = ImMap::default();
// let pool: &mut Vec<Variable> = next_pools.get_mut(next_rank);
// for (symbol, loc_type) in let_con.def_types.iter() {
// let def_type = &loc_type.value; // Replace the contents of this pool with rigid_vars and flex_vars
// pool.clear();
// let var = pool.reserve(rigid_vars.len() + flex_vars.len());
// type_to_var(subs, next_rank, next_pools, cached_aliases, def_type); pool.extend(rigid_vars.iter());
// pool.extend(flex_vars.iter());
// local_def_vars.insert(
// *symbol, // run solver in next pool
// Located {
// value: var, // Add a variable for each def to local_def_vars.
// region: loc_type.region, let mut local_def_vars = ImMap::default();
// },
// ); for (symbol, loc_type) in let_con.def_types.iter() {
// } let def_type = &loc_type.value;
//
// // Solve the assignments' constraints first. let var = type_to_var(
// let State { mempool,
// env: saved_env, subs,
// mark, next_rank,
// } = solve( next_pools,
// &env, cached_aliases,
// state, def_type,
// next_rank, );
// next_pools,
// problems, local_def_vars.insert(
// cached_aliases, *symbol,
// subs, Located {
// &let_con.defs_constraint, value: var,
// ); region: loc_type.region,
// },
// let young_mark = mark; );
// let visit_mark = young_mark.next(); }
// let final_mark = visit_mark.next();
// // Solve the assignments' constraints first.
// debug_assert_eq!( let State {
// { env: saved_env,
// let offenders = next_pools mark,
// .get(next_rank) } = solve(
// .iter() mempool,
// .filter(|var| { &env,
// let current = subs.get_without_compacting( state,
// roc_types::subs::Variable::clone(var), next_rank,
// ); next_pools,
// problems,
// current.rank.into_usize() > next_rank.into_usize() cached_aliases,
// }) subs,
// .collect::<Vec<_>>(); &let_con.defs_constraint,
// );
// let result = offenders.len();
// let young_mark = mark;
// if result > 0 { let visit_mark = young_mark.next();
// dbg!(&subs, &offenders, &let_con.def_types); let final_mark = visit_mark.next();
// }
// debug_assert_eq!(
// result {
// }, let offenders = next_pools
// 0 .get(next_rank)
// ); .iter()
// .filter(|var| {
// // pop pool let current = subs.get_without_compacting(
// generalize(subs, young_mark, visit_mark, next_rank, next_pools); roc_types::subs::Variable::clone(var),
// );
// next_pools.get_mut(next_rank).clear();
// current.rank.into_usize() > next_rank.into_usize()
// // check that things went well })
// debug_assert!({ .collect::<Vec<_>>();
// // NOTE the `subs.redundant` check is added for the uniqueness
// // inference, and does not come from elm. It's unclear whether this is let result = offenders.len();
// // a bug with uniqueness inference (something is redundant that
// // shouldn't be) or that it just never came up in elm. if result > 0 {
// let failing: Vec<_> = rigid_vars dbg!(&subs, &offenders, &let_con.def_types);
// .iter() }
// .filter(|&var| {
// !subs.redundant(*var) result
// && subs.get_without_compacting(*var).rank != Rank::NONE },
// }) 0
// .collect(); );
//
// if !failing.is_empty() { // pop pool
// println!("Rigids {:?}", &rigid_vars); generalize(subs, young_mark, visit_mark, next_rank, next_pools);
// println!("Failing {:?}", failing);
// } next_pools.get_mut(next_rank).clear();
//
// failing.is_empty() // check that things went well
// }); debug_assert!({
// // NOTE the `subs.redundant` check is added for the uniqueness
// let mut new_env = env.clone(); // inference, and does not come from elm. It's unclear whether this is
// for (symbol, loc_var) in local_def_vars.iter() { // a bug with uniqueness inference (something is redundant that
// // when there are duplicates, keep the one from `env` // shouldn't be) or that it just never came up in elm.
// if !new_env.vars_by_symbol.contains_key(&symbol) { let failing: Vec<_> = rigid_vars
// new_env.vars_by_symbol.insert(*symbol, loc_var.value); .iter()
// } .filter(|&var| {
// } !subs.redundant(*var)
// && subs.get_without_compacting(*var).rank != Rank::NONE
// // Note that this vars_by_symbol is the one returned by the })
// // previous call to solve() .collect();
// let temp_state = State {
// env: saved_env, if !failing.is_empty() {
// mark: final_mark, println!("Rigids {:?}", &rigid_vars);
// }; println!("Failing {:?}", failing);
// }
// // Now solve the body, using the new vars_by_symbol which includes
// // the assignments' name-to-variable mappings. failing.is_empty()
// let new_state = solve( });
// &new_env,
// temp_state, let mut new_env = env.clone();
// rank, for (symbol, loc_var) in local_def_vars.iter() {
// next_pools, // when there are duplicates, keep the one from `env`
// problems, if !new_env.vars_by_symbol.contains_key(&symbol) {
// cached_aliases, new_env.vars_by_symbol.insert(*symbol, loc_var.value);
// subs, }
// &ret_con, }
// );
// // Note that this vars_by_symbol is the one returned by the
// for (symbol, loc_var) in local_def_vars { // previous call to solve()
// check_for_infinite_type(subs, problems, symbol, loc_var); let temp_state = State {
// } env: saved_env,
// mark: final_mark,
// new_state };
// }
// } // Now solve the body, using the new vars_by_symbol which includes
// } // the assignments' name-to-variable mappings.
let new_state = solve(
mempool,
&new_env,
temp_state,
rank,
next_pools,
problems,
cached_aliases,
subs,
&ret_con,
);
for (symbol, loc_var) in local_def_vars {
check_for_infinite_type(subs, problems, symbol, loc_var);
}
new_state
}
}
} // _ => todo!("implement {:?}", constraint),
} }
} }
@ -641,7 +661,7 @@ pub fn insert_type_into_subs(mempool: &mut Pool, subs: &mut Subs, typ: &Type2) -
} }
fn type_to_variable( fn type_to_variable(
mempool: &mut Pool, mempool: &Pool,
subs: &mut Subs, subs: &mut Subs,
rank: Rank, rank: Rank,
pools: &mut Pools, pools: &mut Pools,
@ -707,6 +727,95 @@ fn type_to_variable(
register(subs, rank, pools, content) register(subs, rank, pools, content)
} }
Alias(Symbol::BOOL_BOOL, _, _) => roc_types::subs::Variable::BOOL,
Alias(symbol, args, alias_type_id) => {
// TODO cache in uniqueness inference gives problems! all Int's get the same uniqueness var!
// Cache aliases without type arguments. Commonly used aliases like `Int` would otherwise get O(n)
// different variables (once for each occurence). The recursion restriction is required
// for uniqueness types only: recursive aliases "introduce" an unbound uniqueness
// attribute in the body, when
//
// Peano : [ S Peano, Z ]
//
// becomes
//
// Peano : [ S (Attr u Peano), Z ]
//
// This `u` variable can be different between lists, so giving just one variable to
// this type is incorrect.
// TODO does caching work at all with uniqueness types? even Int then hides a uniqueness variable
let alias_type = mempool.get(*alias_type_id);
let is_recursive = false; // alias_type.is_recursive();
let no_args = args.is_empty();
/*
if no_args && !is_recursive {
if let Some(var) = cached.get(symbol) {
return *var;
}
}
*/
let mut arg_vars = Vec::with_capacity(args.len());
let mut new_aliases = ImMap::default();
for (arg, arg_type_id) in args.iter(mempool) {
let arg_type = mempool.get(*arg_type_id);
let arg_var = type_to_variable(mempool, subs, rank, pools, cached, arg_type);
let arg_str = arg.as_str(mempool);
arg_vars.push((roc_module::ident::Lowercase::from(arg_str), arg_var));
new_aliases.insert(arg_str, arg_var);
}
let alias_var = type_to_variable(mempool, subs, rank, pools, cached, alias_type);
let content = Content::Alias(*symbol, arg_vars, alias_var);
let result = register(subs, rank, pools, content);
if no_args && !is_recursive {
// cached.insert(*symbol, result);
}
result
}
TagUnion(tags, ext_id) => {
let mut tag_vars = MutMap::default();
let ext = mempool.get(*ext_id);
for (_tag, tag_argument_types) in tags.iter(mempool) {
let mut tag_argument_vars = Vec::with_capacity(tag_argument_types.len());
for arg_type in tag_argument_types.iter(mempool) {
tag_argument_vars.push(type_to_variable(
mempool, subs, rank, pools, cached, arg_type,
));
}
tag_vars.insert(
roc_module::ident::TagName::Private(Symbol::NUM_NUM),
tag_argument_vars,
);
}
let temp_ext_var = type_to_variable(mempool, subs, rank, pools, cached, ext);
let mut ext_tag_vec = Vec::new();
let new_ext_var = match roc_types::pretty_print::chase_ext_tag_union(
subs,
temp_ext_var,
&mut ext_tag_vec,
) {
Ok(()) => roc_types::subs::Variable::EMPTY_TAG_UNION,
Err((new, _)) => new,
};
tag_vars.extend(ext_tag_vec.into_iter());
let content = Content::Structure(FlatType::TagUnion(tag_vars, new_ext_var));
register(subs, rank, pools, content)
}
other => todo!("not implemented {:?}", &other), other => todo!("not implemented {:?}", &other),
// //
// // This case is important for the rank of boolean variables // // This case is important for the rank of boolean variables
@ -723,35 +832,6 @@ fn type_to_variable(
// //
// register(subs, rank, pools, content) // register(subs, rank, pools, content)
// } // }
// TagUnion(tags, ext) => {
// let mut tag_vars = MutMap::default();
//
// for (tag, tag_argument_types) in tags {
// let mut tag_argument_vars = Vec::with_capacity(tag_argument_types.len());
//
// for arg_type in tag_argument_types {
// tag_argument_vars.push(type_to_variable(subs, rank, pools, cached, arg_type));
// }
//
// tag_vars.insert(tag.clone(), tag_argument_vars);
// }
//
// let temp_ext_var = type_to_variable(subs, rank, pools, cached, ext);
// let mut ext_tag_vec = Vec::new();
// let new_ext_var = match roc_types::pretty_print::chase_ext_tag_union(
// subs,
// temp_ext_var,
// &mut ext_tag_vec,
// ) {
// Ok(()) => Variable::EMPTY_TAG_UNION,
// Err((new, _)) => new,
// };
// tag_vars.extend(ext_tag_vec.into_iter());
//
// let content = Content::Structure(FlatType::TagUnion(tag_vars, new_ext_var));
//
// register(subs, rank, pools, content)
// }
// RecursiveTagUnion(rec_var, tags, ext) => { // RecursiveTagUnion(rec_var, tags, ext) => {
// let mut tag_vars = MutMap::default(); // let mut tag_vars = MutMap::default();
// //
@ -792,54 +872,6 @@ fn type_to_variable(
// //
// tag_union_var // tag_union_var
// } // }
// Alias(Symbol::BOOL_BOOL, _, _) => Variable::BOOL,
// Alias(symbol, args, alias_type) => {
// // TODO cache in uniqueness inference gives problems! all Int's get the same uniqueness var!
// // Cache aliases without type arguments. Commonly used aliases like `Int` would otherwise get O(n)
// // different variables (once for each occurence). The recursion restriction is required
// // for uniqueness types only: recursive aliases "introduce" an unbound uniqueness
// // attribute in the body, when
// //
// // Peano : [ S Peano, Z ]
// //
// // becomes
// //
// // Peano : [ S (Attr u Peano), Z ]
// //
// // This `u` variable can be different between lists, so giving just one variable to
// // this type is incorrect.
// // TODO does caching work at all with uniqueness types? even Int then hides a uniqueness variable
// let is_recursive = alias_type.is_recursive();
// let no_args = args.is_empty();
// /*
// if no_args && !is_recursive {
// if let Some(var) = cached.get(symbol) {
// return *var;
// }
// }
// */
//
// let mut arg_vars = Vec::with_capacity(args.len());
// let mut new_aliases = ImMap::default();
//
// for (arg, arg_type) in args {
// let arg_var = type_to_variable(subs, rank, pools, cached, arg_type);
//
// arg_vars.push((arg.clone(), arg_var));
// new_aliases.insert(arg.clone(), arg_var);
// }
//
// let alias_var = type_to_variable(subs, rank, pools, cached, alias_type);
// let content = Content::Alias(*symbol, arg_vars, alias_var);
//
// let result = register(subs, rank, pools, content);
//
// if no_args && !is_recursive {
// // cached.insert(*symbol, result);
// }
//
// result
// }
// HostExposedAlias { // HostExposedAlias {
// name: symbol, // name: symbol,
// arguments: args, // arguments: args,

View file

@ -60,7 +60,13 @@ pub enum Problem2 {
impl ShallowClone for Type2 { impl ShallowClone for Type2 {
fn shallow_clone(&self) -> Self { fn shallow_clone(&self) -> Self {
todo!() match self {
Self::Variable(var) => Self::Variable(*var),
Self::Alias(symbol, pool_vec, type_id) => {
Self::Alias(*symbol, pool_vec.shallow_clone(), type_id.clone())
}
rest => todo!("{:?}", rest),
}
} }
} }

View file

@ -13,6 +13,19 @@ impl TextPos {
column: self.column + 1, column: self.column + 1,
} }
} }
pub fn decrement_col(&self) -> TextPos {
let new_col = if self.column > 0 {
self.column - 1
} else {
self.column
};
TextPos {
line: self.line,
column: new_col,
}
}
} }
impl Ord for TextPos { impl Ord for TextPos {

View file

@ -16,6 +16,7 @@ use roc_editor::lang::{
types::Type2, types::Type2,
}; };
use roc_module::ident::Lowercase; use roc_module::ident::Lowercase;
use roc_module::symbol::Interns;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_module::symbol::{IdentIds, ModuleIds}; use roc_module::symbol::{IdentIds, ModuleIds};
use roc_region::all::Region; use roc_region::all::Region;
@ -83,6 +84,7 @@ fn infer_eq(actual: &str, expected_str: &str) {
match expr2_result { match expr2_result {
Ok((expr, _)) => { Ok((expr, _)) => {
let constraint = constrain_expr( let constraint = constrain_expr(
&code_arena,
&mut env, &mut env,
&expr, &expr,
Expected::NoExpectation(Type2::Variable(var)), Expected::NoExpectation(Type2::Variable(var)),
@ -92,6 +94,7 @@ fn infer_eq(actual: &str, expected_str: &str) {
let Env { let Env {
pool, pool,
var_store: ref_var_store, var_store: ref_var_store,
dep_idents,
.. ..
} = env; } = env;
@ -111,7 +114,11 @@ fn infer_eq(actual: &str, expected_str: &str) {
let content = subs.get(var).content; let content = subs.get(var).content;
let actual_str = content_to_string(content, &subs, mod_id, &Default::default()); let interns = Interns {
module_ids,
all_ident_ids: dep_idents,
};
let actual_str = content_to_string(content, &subs, mod_id, &interns);
assert_eq!(actual_str, expected_str); assert_eq!(actual_str, expected_str);
} }
@ -142,3 +149,15 @@ fn constrain_empty_record() {
"{}", "{}",
) )
} }
#[test]
fn constrain_small_int() {
infer_eq(
indoc!(
r#"
12
"#
),
"Int *",
)
}