pull in trunk, moved all pipelines to single file, renamed model to ed_model

This commit is contained in:
Anton-4 2021-01-08 16:17:59 +01:00
commit 659a77da9f
19 changed files with 2386 additions and 111 deletions

2
Cargo.lock generated
View file

@ -2672,6 +2672,8 @@ dependencies = [
"log",
"maplit",
"page_size",
"pest",
"pest_derive",
"pretty_assertions",
"quickcheck",
"quickcheck_macros",

View file

@ -484,6 +484,34 @@ fn get_inplace_from_layout(layout: &Layout<'_>) -> InPlace {
}
}
pub fn int_with_precision<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
value: i128,
precision: &Builtin,
) -> IntValue<'ctx> {
match precision {
Builtin::Usize => ptr_int(env.context, env.ptr_bytes).const_int(value as u64, false),
Builtin::Int128 => const_i128(env, value),
Builtin::Int64 => env.context.i64_type().const_int(value as u64, false),
Builtin::Int32 => env.context.i32_type().const_int(value as u64, false),
Builtin::Int16 => env.context.i16_type().const_int(value as u64, false),
Builtin::Int8 => env.context.i8_type().const_int(value as u64, false),
_ => panic!("Invalid layout for int literal = {:?}", precision),
}
}
pub fn float_with_precision<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
value: f64,
precision: &Builtin,
) -> FloatValue<'ctx> {
match precision {
Builtin::Float64 => env.context.f64_type().const_float(value),
Builtin::Float32 => env.context.f32_type().const_float(value),
_ => panic!("Invalid layout for float literal = {:?}", precision),
}
}
pub fn build_exp_literal<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout: &Layout<'_>,
@ -492,22 +520,16 @@ pub fn build_exp_literal<'a, 'ctx, 'env>(
use roc_mono::ir::Literal::*;
match literal {
Int(int) =>
(match layout {
Layout::Builtin(Builtin::Usize) => ptr_int(env.context, env.ptr_bytes),
Layout::Builtin(Builtin::Int128) => env.context.i128_type(), /* TODO file an issue: you can't currently have an int literal bigger than 64 bits long, and also (as we see here), you can't currently have (at least in Inkwell) a when-branch with an i128 literal in its pattren */
Layout::Builtin(Builtin::Int64) => env.context.i64_type(),
Layout::Builtin(Builtin::Int32) => env.context.i32_type(),
Layout::Builtin(Builtin::Int16) => env.context.i16_type(),
Layout::Builtin(Builtin::Int8) => env.context.i8_type(),
Int(int) => match layout {
Layout::Builtin(builtin) => int_with_precision(env, *int as i128, builtin).into(),
_ => panic!("Invalid layout for int literal = {:?}", layout),
}).const_int(*int as u64, false).into(),
Float(num) =>
(match layout {
Layout::Builtin(Builtin::Float64) => env.context.f64_type(),
Layout::Builtin(Builtin::Float32) => env.context.f32_type(),
},
Float(float) => match layout {
Layout::Builtin(builtin) => float_with_precision(env, *float, builtin).into(),
_ => panic!("Invalid layout for float literal = {:?}", layout),
}).const_float(*num).into(),
},
Bool(b) => env.context.bool_type().const_int(*b as u64, false).into(),
Byte(b) => env.context.i8_type().const_int(*b as u64, false).into(),
Str(str_literal) => {
@ -1494,9 +1516,9 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
call,
layout,
pass,
fail: roc_mono::ir::Stmt::Unreachable,
fail: roc_mono::ir::Stmt::Rethrow,
} => {
// when the fail case is just Unreachable, there is no cleanup work to do
// when the fail case is just Rethrow, there is no cleanup work to do
// so we can just treat this invoke as a normal call
let stmt =
roc_mono::ir::Stmt::Let(*symbol, Expr::Call(call.clone()), layout.clone(), pass);
@ -1561,7 +1583,7 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
}
},
Unreachable => {
Rethrow => {
cxa_rethrow_exception(env);
// used in exception handling
@ -1783,6 +1805,22 @@ struct SwitchArgsIr<'a, 'ctx> {
pub ret_type: BasicTypeEnum<'ctx>,
}
fn const_i128<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, value: i128) -> IntValue<'ctx> {
// TODO verify the order [a, b] is correct for larger numbers when we can parse them
debug_assert!(value <= i64::MAX as i128);
// truncate the lower 64 bits
let value = value as u128;
let a = value as u64;
// get the upper 64 bits
let b = (value >> 64) as u64;
env.context
.i128_type()
.const_int_arbitrary_precision(&[a, b])
}
fn build_switch_ir<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_ids: &mut LayoutIds<'a>,
@ -1860,7 +1898,7 @@ fn build_switch_ir<'a, 'ctx, 'env>(
ptr_int(env.context, env.ptr_bytes).const_int(*int as u64, false)
}
Layout::Builtin(Builtin::Int64) => context.i64_type().const_int(*int as u64, false),
Layout::Builtin(Builtin::Int128) => context.i128_type().const_int(*int as u64, false), /* TODO file an issue: you can't currently have an int literal bigger than 64 bits long, and also (as we see here), you can't currently have (at least in Inkwell) a when-branch with an i128 literal in its pattren */
Layout::Builtin(Builtin::Int128) => const_i128(env, *int as i128),
Layout::Builtin(Builtin::Int32) => context.i32_type().const_int(*int as u64, false),
Layout::Builtin(Builtin::Int16) => context.i16_type().const_int(*int as u64, false),
Layout::Builtin(Builtin::Int8) => context.i8_type().const_int(*int as u64, false),

View file

@ -1273,7 +1273,7 @@ mod gen_list {
app "quicksort" provides [ main ] to "./platform"
swap : Int *, Int *, List a -> List a
swap : Nat, Nat, List a -> List a
swap = \i, j, list ->
when Pair (List.get list i) (List.get list j) is
Pair (Ok atI) (Ok atJ) ->
@ -1396,7 +1396,7 @@ mod gen_list {
quicksortHelp list 0 (n - 1)
quicksortHelp : List (Num a), Int *, Int * -> List (Num a)
quicksortHelp : List (Num a), Nat, Nat -> List (Num a)
quicksortHelp = \list, low, high ->
if low < high then
when partition low high list is
@ -1408,7 +1408,7 @@ mod gen_list {
list
swap : Int *, Int *, List a -> List a
swap : Nat, Nat, List a -> List a
swap = \i, j, list ->
when Pair (List.get list i) (List.get list j) is
Pair (Ok atI) (Ok atJ) ->
@ -1419,7 +1419,7 @@ mod gen_list {
_ ->
[]
partition : Int *, Int *, List (Num a) -> [ Pair (Int *) (List (Num a)) ]
partition : Nat, Nat, List (Num a) -> [ Pair Nat (List (Num a)) ]
partition = \low, high, initialList ->
when List.get initialList high is
Ok pivot ->
@ -1431,7 +1431,7 @@ mod gen_list {
Pair (low - 1) initialList
partitionHelp : Int *, Int *, List (Num a), Int *, (Num a) -> [ Pair (Int *) (List (Num a)) ]
partitionHelp : Nat, Nat, List (Num a), Nat, (Num a) -> [ Pair Nat (List (Num a)) ]
partitionHelp = \i, j, list, high, pivot ->
if j < high then
when List.get list j is
@ -1466,7 +1466,7 @@ mod gen_list {
quicksortHelp list 0 (List.len list - 1)
quicksortHelp : List (Num a), Int *, Int * -> List (Num a)
quicksortHelp : List (Num a), Nat, Nat -> List (Num a)
quicksortHelp = \list, low, high ->
if low < high then
when partition low high list is
@ -1478,7 +1478,7 @@ mod gen_list {
list
swap : Int *, Int *, List a -> List a
swap : Nat, Nat, List a -> List a
swap = \i, j, list ->
when Pair (List.get list i) (List.get list j) is
Pair (Ok atI) (Ok atJ) ->
@ -1489,7 +1489,7 @@ mod gen_list {
_ ->
[]
partition : Int *, Int *, List (Num a) -> [ Pair (Int *) (List (Num a)) ]
partition : Nat, Nat, List (Num a) -> [ Pair Nat (List (Num a)) ]
partition = \low, high, initialList ->
when List.get initialList high is
Ok pivot ->
@ -1501,7 +1501,7 @@ mod gen_list {
Pair (low - 1) initialList
partitionHelp : Int *, Int *, List (Num a), Int *, Num a -> [ Pair (Int *) (List (Num a)) ]
partitionHelp : Nat, Nat, List (Num a), Nat, Num a -> [ Pair Nat (List (Num a)) ]
partitionHelp = \i, j, list, high, pivot ->
# if j < high then
if False then
@ -1539,7 +1539,7 @@ mod gen_list {
quicksortHelp list 0 (List.len list - 1)
quicksortHelp : List (Num a), Int *, Int * -> List (Num a)
quicksortHelp : List (Num a), Nat, Nat -> List (Num a)
quicksortHelp = \list, low, high ->
if low < high then
when partition low high list is
@ -1551,7 +1551,7 @@ mod gen_list {
list
swap : Int *, Int *, List a -> List a
swap : Nat, Nat, List a -> List a
swap = \i, j, list ->
when Pair (List.get list i) (List.get list j) is
Pair (Ok atI) (Ok atJ) ->
@ -1562,7 +1562,7 @@ mod gen_list {
_ ->
[]
partition : Int *, Int *, List (Num a) -> [ Pair (Int *) (List (Num a)) ]
partition : Nat, Nat, List (Num a) -> [ Pair Nat (List (Num a)) ]
partition = \low, high, initialList ->
when List.get initialList high is
Ok pivot ->
@ -1574,7 +1574,7 @@ mod gen_list {
Pair (low - 1) initialList
partitionHelp : Int *, Int *, List (Num a), Int *, Num a -> [ Pair (Int *) (List (Num a)) ]
partitionHelp : Nat, Nat, List (Num a), Nat, Num a -> [ Pair Nat (List (Num a)) ]
partitionHelp = \i, j, list, high, pivot ->
if j < high then
when List.get list j is
@ -1740,6 +1740,7 @@ mod gen_list {
assert_evals_to!("[[2]] != [[1]]", true, bool);
}
#[test]
#[should_panic(expected = r#"Roc failed with message: "integer addition overflowed!"#)]
fn cleanup_because_exception() {
assert_evals_to!(

View file

@ -339,7 +339,7 @@ where
Stmt::Ret(sym) => {
self.set_last_seen(*sym, stmt);
}
Stmt::Unreachable => {}
Stmt::Rethrow => {}
Stmt::Inc(sym, following) => {
self.set_last_seen(*sym, stmt);
self.scan_ast(following);

View file

@ -170,7 +170,7 @@ impl<'a> ParamMap<'a> {
}
Inc(_, _) | Dec(_, _) => unreachable!("these have not been introduced yet"),
Ret(_) | Unreachable | Jump(_, _) | RuntimeError(_) => {
Ret(_) | Rethrow | Jump(_, _) | RuntimeError(_) => {
// these are terminal, do nothing
}
}
@ -515,7 +515,7 @@ impl<'a> BorrowInfState<'a> {
}
Inc(_, _) | Dec(_, _) => unreachable!("these have not been introduced yet"),
Ret(_) | RuntimeError(_) | Unreachable => {
Ret(_) | RuntimeError(_) | Rethrow => {
// these are terminal, do nothing
}
}

View file

@ -66,7 +66,7 @@ pub enum Test<'a> {
union: crate::exhaustive::Union,
arguments: Vec<(Pattern<'a>, Layout<'a>)>,
},
IsInt(i64),
IsInt(i128),
// float patterns are stored as u64 so they are comparable/hashable
IsFloat(u64),
IsStr(Box<str>),
@ -445,10 +445,10 @@ fn test_at_path<'a>(selected_path: &Path, branch: &Branch<'a>, all_tests: &mut V
num_alts: union.alternatives.len(),
});
}
IntLiteral(_, v) => {
IntLiteral(v) => {
all_tests.push(guarded(IsInt(*v)));
}
FloatLiteral(_, v) => {
FloatLiteral(v) => {
all_tests.push(IsFloat(*v));
}
StrLiteral(v) => {
@ -636,7 +636,7 @@ fn to_relevant_branch_help<'a>(
_ => None,
},
IntLiteral(_, int) => match test {
IntLiteral(int) => match test {
IsInt(is_int) if int == *is_int => {
start.extend(end);
Some(Branch {
@ -647,7 +647,7 @@ fn to_relevant_branch_help<'a>(
_ => None,
},
FloatLiteral(_, float) => match test {
FloatLiteral(float) => match test {
IsFloat(test_float) if float == *test_float => {
start.extend(end);
Some(Branch {
@ -740,8 +740,8 @@ fn needs_tests(pattern: &Pattern) -> bool {
| AppliedTag { .. }
| BitLiteral { .. }
| EnumLiteral { .. }
| IntLiteral(_, _)
| FloatLiteral(_, _)
| IntLiteral(_)
| FloatLiteral(_)
| StrLiteral(_) => true,
}
}
@ -1092,7 +1092,9 @@ fn test_to_equality<'a>(
)
}
Test::IsInt(test_int) => {
let lhs = Expr::Literal(Literal::Int(test_int));
// TODO don't downcast i128 here
debug_assert!(test_int <= i64::MAX as i128);
let lhs = Expr::Literal(Literal::Int(test_int as i64));
let lhs_symbol = env.unique_symbol();
stores.push((lhs_symbol, Layout::Builtin(Builtin::Int64), lhs));

View file

@ -37,7 +37,7 @@ pub enum Pattern {
#[derive(Clone, Debug, PartialEq)]
pub enum Literal {
Int(i64),
Int(i128),
Bit(bool),
Byte(u8),
Float(u64),
@ -48,8 +48,8 @@ fn simplify(pattern: &crate::ir::Pattern) -> Pattern {
use crate::ir::Pattern::*;
match pattern {
IntLiteral(_, v) => Literal(Literal::Int(*v)),
FloatLiteral(_, v) => Literal(Literal::Float(*v)),
IntLiteral(v) => Literal(Literal::Int(*v)),
FloatLiteral(v) => Literal(Literal::Float(*v)),
StrLiteral(v) => Literal(Literal::Str(v.clone())),
// To make sure these are exhaustive, we have to "fake" a union here

View file

@ -50,7 +50,7 @@ pub fn occuring_variables(stmt: &Stmt<'_>) -> (MutSet<Symbol>, MutSet<Symbol>) {
result.insert(*symbol);
}
Unreachable => {}
Rethrow => {}
Inc(symbol, cont) | Dec(symbol, cont) => {
result.insert(*symbol);
@ -792,7 +792,7 @@ impl<'a> Context<'a> {
}
}
Unreachable => (stmt, MutSet::default()),
Rethrow => (stmt, MutSet::default()),
Jump(j, xs) => {
let empty = MutSet::default();
@ -953,7 +953,7 @@ pub fn collect_stmt(
vars
}
Unreachable => vars,
Rethrow => vars,
RuntimeError(_) => vars,
}

View file

@ -762,7 +762,7 @@ pub enum Stmt<'a> {
ret_layout: Layout<'a>,
},
Ret(Symbol),
Unreachable,
Rethrow,
Inc(Symbol, &'a Stmt<'a>),
Dec(Symbol, &'a Stmt<'a>),
Join {
@ -911,11 +911,6 @@ pub enum CallType<'a> {
},
}
// x = f a b c; S
//
//
// invoke x = f a b c in S else Unreachable
#[derive(Clone, Debug, PartialEq)]
pub enum Expr<'a> {
Literal(Literal<'a>),
@ -1133,7 +1128,7 @@ impl<'a> Stmt<'a> {
symbol,
call,
pass,
fail: Stmt::Unreachable,
fail: Stmt::Rethrow,
..
} => alloc
.text("let ")
@ -1166,7 +1161,7 @@ impl<'a> Stmt<'a> {
.append(symbol_to_doc(alloc, *symbol))
.append(";"),
Unreachable => alloc.text("unreachable;"),
Rethrow => alloc.text("unreachable;"),
Switch {
cond_symbol,
@ -4572,7 +4567,7 @@ fn substitute_in_stmt_help<'a>(
}
}
Unreachable => None,
Rethrow => None,
RuntimeError(_) => None,
}
@ -4774,8 +4769,8 @@ fn store_pattern<'a>(
Underscore => {
// do nothing
}
IntLiteral(_, _)
| FloatLiteral(_, _)
IntLiteral(_)
| FloatLiteral(_)
| EnumLiteral { .. }
| BitLiteral { .. }
| StrLiteral(_) => {}
@ -4814,8 +4809,8 @@ fn store_pattern<'a>(
Underscore => {
// ignore
}
IntLiteral(_, _)
| FloatLiteral(_, _)
IntLiteral(_)
| FloatLiteral(_)
| EnumLiteral { .. }
| BitLiteral { .. }
| StrLiteral(_) => {}
@ -4909,8 +4904,8 @@ fn store_record_destruct<'a>(
//
// internally. But `y` is never used, so we must make sure it't not stored/loaded.
}
IntLiteral(_, _)
| FloatLiteral(_, _)
IntLiteral(_)
| FloatLiteral(_)
| EnumLiteral { .. }
| BitLiteral { .. }
| StrLiteral(_) => {}
@ -5293,7 +5288,7 @@ fn build_call<'a>(
hole: &'a Stmt<'a>,
) -> Stmt<'a> {
if can_throw_exception(&call) {
let fail = env.arena.alloc(Stmt::Unreachable);
let fail = env.arena.alloc(Stmt::Rethrow);
Stmt::Invoke {
symbol: assigned,
call,
@ -5654,8 +5649,8 @@ fn call_by_name<'a>(
pub enum Pattern<'a> {
Identifier(Symbol),
Underscore,
IntLiteral(Variable, i64),
FloatLiteral(Variable, u64),
IntLiteral(i128),
FloatLiteral(u64),
BitLiteral {
value: bool,
tag_name: TagName,
@ -5728,10 +5723,8 @@ fn from_can_pattern_help<'a>(
match can_pattern {
Underscore => Ok(Pattern::Underscore),
Identifier(symbol) => Ok(Pattern::Identifier(*symbol)),
IntLiteral(precision_var, int) => Ok(Pattern::IntLiteral(*precision_var, *int)),
FloatLiteral(precision_var, float) => {
Ok(Pattern::FloatLiteral(*precision_var, f64::to_bits(*float)))
}
IntLiteral(_, int) => Ok(Pattern::IntLiteral(*int as i128)),
FloatLiteral(_, float) => Ok(Pattern::FloatLiteral(f64::to_bits(*float))),
StrLiteral(v) => Ok(Pattern::StrLiteral(v.clone())),
Shadowed(region, ident) => Err(RuntimeError::Shadowing {
original_region: *region,
@ -5744,10 +5737,10 @@ fn from_can_pattern_help<'a>(
}
NumLiteral(var, num) => {
match num_argument_to_int_or_float(env.subs, env.ptr_bytes, *var, false) {
IntOrFloat::SignedIntType(_) => Ok(Pattern::IntLiteral(*var, *num)),
IntOrFloat::UnsignedIntType(_) => Ok(Pattern::IntLiteral(*var, *num)),
IntOrFloat::BinaryFloatType(_) => Ok(Pattern::FloatLiteral(*var, *num as u64)),
IntOrFloat::DecimalFloatType(_) => Ok(Pattern::FloatLiteral(*var, *num as u64)),
IntOrFloat::SignedIntType(_) => Ok(Pattern::IntLiteral(*num as i128)),
IntOrFloat::UnsignedIntType(_) => Ok(Pattern::IntLiteral(*num as i128)),
IntOrFloat::BinaryFloatType(_) => Ok(Pattern::FloatLiteral(*num as u64)),
IntOrFloat::DecimalFloatType(_) => Ok(Pattern::FloatLiteral(*num as u64)),
}
}

View file

@ -102,7 +102,7 @@ fn insert_jumps<'a>(
pass: Stmt::Ret(rsym),
..
} if needle == *fsym && symbol == rsym => {
debug_assert_eq!(fail, &&Stmt::Unreachable);
debug_assert_eq!(fail, &&Stmt::Rethrow);
// replace the call and return with a jump
@ -237,7 +237,7 @@ fn insert_jumps<'a>(
None => None,
},
Unreachable => None,
Rethrow => None,
Ret(_) => None,
Jump(_, _) => None,
RuntimeError(_) => None,

View file

@ -66,6 +66,8 @@ cgmath = "0.17.0"
itertools = "0.9.0"
snafu = { version = "0.6", features = ["backtraces"] }
colored = "2"
pest = "2.1"
pest_derive = "2.1"
[dependencies.bytemuck]

View file

@ -55,8 +55,8 @@ These are potentially inspirational resources for the editor's design.
### Productivity features
* When refactoring;
- cutting and pasting code to a new file should automatically add imports to the new file and delete them from the old file.
- When renaming a function for example, references in comments should be brought to the user's attention.
- Cutting and pasting code to a new file should automatically add imports to the new file and delete them from the old file.
- Ability to link e.g. variable name in comments to actual variable name. Comment is automatically updated when variable name is changed.
* Automatically create all "arms" when pattern matching after entering `when var is` based on the type.
- All `when ... is` should be updated if the type is changed, e.g. adding Indigo to the Color type should add an arm everywhere where `when color is` is used.

View file

@ -0,0 +1,71 @@
use super::ortho::{init_ortho, OrthoResources};
use super::vertex::Vertex;
pub struct RectResources {
pub pipeline: wgpu::RenderPipeline,
pub ortho: OrthoResources,
}
pub fn make_rect_pipeline(
gpu_device: &wgpu::Device,
swap_chain_descr: &wgpu::SwapChainDescriptor,
) -> RectResources {
let ortho = init_ortho(swap_chain_descr.width, swap_chain_descr.height, gpu_device);
let pipeline_layout = gpu_device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[&ortho.bind_group_layout],
push_constant_ranges: &[],
label: Some("Rectangle pipeline layout"),
});
let pipeline = create_render_pipeline(
&gpu_device,
&pipeline_layout,
swap_chain_descr.format,
&[Vertex::DESC],
wgpu::include_spirv!("../shaders/rect.vert.spv"),
wgpu::include_spirv!("../shaders/rect.frag.spv"),
);
RectResources { pipeline, ortho }
}
pub fn create_render_pipeline(
device: &wgpu::Device,
layout: &wgpu::PipelineLayout,
color_format: wgpu::TextureFormat,
vertex_descs: &[wgpu::VertexBufferDescriptor],
vs_src: wgpu::ShaderModuleSource,
fs_src: wgpu::ShaderModuleSource,
) -> wgpu::RenderPipeline {
let vs_module = device.create_shader_module(vs_src);
let fs_module = device.create_shader_module(fs_src);
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Render pipeline"),
layout: Some(&layout),
vertex_stage: wgpu::ProgrammableStageDescriptor {
module: &vs_module,
entry_point: "main",
},
fragment_stage: Some(wgpu::ProgrammableStageDescriptor {
module: &fs_module,
entry_point: "main",
}),
rasterization_state: None,
primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[wgpu::ColorStateDescriptor {
format: color_format,
color_blend: wgpu::BlendDescriptor::REPLACE,
alpha_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL,
}],
depth_stencil_state: None,
sample_count: 1,
sample_mask: !0,
alpha_to_coverage_enabled: false,
vertex_state: wgpu::VertexStateDescriptor {
index_format: wgpu::IndexFormat::Uint32,
vertex_buffers: vertex_descs,
},
})
}

View file

@ -8,6 +8,11 @@
// See this link to learn wgpu: https://sotrh.github.io/learn-wgpu/
extern crate pest;
#[cfg(test)]
#[macro_use]
extern crate pest_derive;
use crate::error::EdError::MissingGlyphDims;
use crate::error::{print_err, EdResult};
use crate::graphics::colors::{CARET_COLOR, CODE_COLOR, TXT_COLOR};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,52 @@
use crate::graphics::primitives::rect::Rect;
use std::cmp::Ordering;
#[derive(Debug)]
pub struct EdModel {
pub lines: Vec<String>,
pub caret_pos: Position,
pub selection_opt: Option<RawSelection>,
pub glyph_dim_rect_opt: Option<Rect>,
}
pub fn init_model() -> EdModel {
EdModel {
lines: vec![String::new()],
caret_pos: Position { line: 0, column: 0 },
selection_opt: None,
glyph_dim_rect_opt: None,
}
}
//Is model.rs the right place for these structs?
#[derive(Debug, Copy, Clone)]
pub struct Position {
pub line: usize,
pub column: usize,
}
impl Ord for Position {
fn cmp(&self, other: &Self) -> Ordering {
(self.line, self.column).cmp(&(other.line, other.column))
}
}
impl PartialOrd for Position {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for Position {
fn eq(&self, other: &Self) -> bool {
(self.line, self.column) == (other.line, other.column)
}
}
impl Eq for Position {}
#[derive(Debug, Copy, Clone)]
pub struct RawSelection {
pub start_pos: Position,
pub end_pos: Position,
}

View file

@ -12,7 +12,12 @@ pub fn move_caret_left(
let old_line_nr = old_caret_pos.line;
let old_col_nr = old_caret_pos.column;
let (line_nr, col_nr) = if old_col_nr == 0 {
let (line_nr, col_nr) = if old_selection_opt.is_some() && !shift_pressed {
match old_selection_opt {
Some(old_selection) => (old_selection.start_pos.line, old_selection.start_pos.column),
None => unreachable!(),
}
} else if old_col_nr == 0 {
if old_line_nr == 0 {
(0, 0)
} else if let Some(curr_line) = lines.get(old_line_nr - 1) {
@ -31,6 +36,16 @@ pub fn move_caret_left(
let new_selection_opt = if shift_pressed {
if let Some(old_selection) = old_selection_opt {
if old_caret_pos >= old_selection.end_pos {
if new_caret_pos == old_selection.start_pos {
None
} else {
Some(RawSelection {
start_pos: old_selection.start_pos,
end_pos: new_caret_pos,
})
}
} else {
Some(RawSelection {
start_pos: Position {
line: line_nr,
@ -38,6 +53,7 @@ pub fn move_caret_left(
},
end_pos: old_selection.end_pos,
})
}
} else if !(old_line_nr == line_nr && old_col_nr == col_nr) {
Some(RawSelection {
start_pos: Position {
@ -68,7 +84,12 @@ pub fn move_caret_right(
let old_line_nr = old_caret_pos.line;
let old_col_nr = old_caret_pos.column;
let (line_nr, col_nr) = if let Some(curr_line) = lines.get(old_line_nr) {
let (line_nr, col_nr) = if old_selection_opt.is_some() && !shift_pressed {
match old_selection_opt {
Some(old_selection) => (old_selection.end_pos.line, old_selection.end_pos.column),
None => unreachable!(),
}
} else if let Some(curr_line) = lines.get(old_line_nr) {
if let Some(last_char) = curr_line.chars().last() {
if is_newline(&last_char) {
if old_col_nr + 1 > curr_line.len() - 1 {
@ -95,6 +116,16 @@ pub fn move_caret_right(
let new_selection_opt = if shift_pressed {
if let Some(old_selection) = old_selection_opt {
if old_caret_pos <= old_selection.start_pos {
if new_caret_pos == old_selection.end_pos {
None
} else {
Some(RawSelection {
start_pos: new_caret_pos,
end_pos: old_selection.end_pos,
})
}
} else {
Some(RawSelection {
start_pos: old_selection.start_pos,
end_pos: Position {
@ -102,6 +133,7 @@ pub fn move_caret_right(
column: col_nr,
},
})
}
} else if !(old_line_nr == line_nr && old_col_nr == col_nr) {
Some(RawSelection {
start_pos: Position {
@ -132,10 +164,15 @@ pub fn move_caret_up(
let old_line_nr = old_caret_pos.line;
let old_col_nr = old_caret_pos.column;
let (line_nr, col_nr) = if old_line_nr == 0 {
(old_line_nr, old_col_nr)
let (line_nr, col_nr) = if old_selection_opt.is_some() && !shift_pressed {
match old_selection_opt {
Some(old_selection) => (old_selection.start_pos.line, old_selection.start_pos.column),
None => unreachable!(),
}
} else if old_line_nr == 0 {
(old_line_nr, 0)
} else if let Some(prev_line) = lines.get(old_line_nr - 1) {
if prev_line.len() < old_col_nr {
if prev_line.len() <= old_col_nr {
(old_line_nr - 1, prev_line.len() - 1)
} else {
(old_line_nr - 1, old_col_nr)
@ -151,10 +188,21 @@ pub fn move_caret_up(
let new_selection_opt = if shift_pressed {
if let Some(old_selection) = old_selection_opt {
if old_selection.end_pos <= old_caret_pos {
if new_caret_pos == old_selection.start_pos {
None
} else {
Some(RawSelection {
start_pos: min(old_selection.start_pos, new_caret_pos),
end_pos: max(old_selection.start_pos, new_caret_pos),
})
}
} else {
Some(RawSelection {
start_pos: new_caret_pos,
end_pos: old_selection.end_pos,
})
}
} else if !(old_line_nr == line_nr && old_col_nr == col_nr) {
Some(RawSelection {
start_pos: min(old_caret_pos, new_caret_pos),
@ -179,10 +227,19 @@ pub fn move_caret_down(
let old_line_nr = old_caret_pos.line;
let old_col_nr = old_caret_pos.column;
let (line_nr, col_nr) = if old_line_nr + 1 >= lines.len() {
(old_line_nr, old_col_nr)
let (line_nr, col_nr) = if old_selection_opt.is_some() && !shift_pressed {
match old_selection_opt {
Some(old_selection) => (old_selection.end_pos.line, old_selection.end_pos.column),
None => unreachable!(),
}
} else if old_line_nr + 1 >= lines.len() {
if let Some(curr_line) = lines.get(old_line_nr) {
(old_line_nr, curr_line.len())
} else {
unreachable!()
}
} else if let Some(next_line) = lines.get(old_line_nr + 1) {
if next_line.len() < old_col_nr {
if next_line.len() <= old_col_nr {
if let Some(last_char) = next_line.chars().last() {
if is_newline(&last_char) {
(old_line_nr + 1, next_line.len() - 1)
@ -206,10 +263,21 @@ pub fn move_caret_down(
let new_selection_opt = if shift_pressed {
if let Some(old_selection) = old_selection_opt {
if old_caret_pos <= old_selection.start_pos {
if new_caret_pos == old_selection.end_pos {
None
} else {
Some(RawSelection {
start_pos: min(old_selection.end_pos, new_caret_pos),
end_pos: max(old_selection.end_pos, new_caret_pos),
})
}
} else {
Some(RawSelection {
start_pos: old_selection.start_pos,
end_pos: new_caret_pos,
})
}
} else if !(old_line_nr == line_nr && old_col_nr == col_nr) {
Some(RawSelection {
start_pos: min(old_caret_pos, new_caret_pos),

View file

@ -1,5 +1,5 @@
pub fn is_newline(char_ref: &char) -> bool {
let newline_codes = vec!['\u{d}'];
let newline_codes = vec!['\u{d}', '\n'];
newline_codes.contains(char_ref)
}

View file

@ -0,0 +1,11 @@
text = { (ASCII_ALPHANUMERIC | " " | "\t" | "\n")* }
caret = {"|"}
optSelStart = { "["{0,1} }
optSelEnd = { "]"{0,1} }
optCaret = { caret{0,1} }
linesWithSelect = { SOI ~ text ~ optCaret ~ text ~ optSelStart ~ text ~ optCaret ~ text ~ optCaret ~ text ~ optSelEnd ~ text ~ optCaret ~ text ~ EOI}