mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 00:01:16 +00:00
Merge branch 'trunk' into parser
This commit is contained in:
commit
bae11e4da5
17 changed files with 1533 additions and 808 deletions
|
@ -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>,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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 },
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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 } } } } } } } }"],
|
||||||
|
|
156
editor/src/editor/mvc/int_update.rs
Normal file
156
editor/src/editor/mvc/int_update.rs
Normal 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(),
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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.";
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 *",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue