Compare commits
3 commits
Author | SHA1 | Date | |
---|---|---|---|
f4e71e46cb | |||
c33dab5644 | |||
8f329e091d |
109 changed files with 7401 additions and 6464 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,5 +1,3 @@
|
||||||
.pijul
|
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
/target
|
/target
|
||||||
*~
|
*~
|
||||||
\#*\#
|
|
14
Cargo.toml
14
Cargo.toml
|
@ -1,10 +1,12 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"nested",
|
"nested",
|
||||||
# "terminal/display_server",
|
"terminal/display_server",
|
||||||
# "terminal/ansi_parser",
|
"terminal/ansi_parser",
|
||||||
# "math/str2int",
|
"shell",
|
||||||
# "math/int2str",
|
"sdf_editor",
|
||||||
# "math/radix_transform",
|
"math/str2int",
|
||||||
# "math/fib"
|
"math/int2str",
|
||||||
|
"math/radix_transform",
|
||||||
|
"math/fib"
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{Read, Write},
|
io::{Read, Write},
|
||||||
os::unix::io::FromRawFd,
|
os::unix::io::FromRawFd
|
||||||
};
|
};
|
||||||
|
|
||||||
fn fib(n: u64) -> u64 {
|
fn fib(n: u64) -> u64 {
|
||||||
|
@ -9,7 +10,7 @@ fn fib(n: u64) -> u64 {
|
||||||
let mut y1 = 1;
|
let mut y1 = 1;
|
||||||
let mut y2 = 0;
|
let mut y2 = 0;
|
||||||
|
|
||||||
for _ in 0..n {
|
for _ in 0 .. n {
|
||||||
y = y1 + y2;
|
y = y1 + y2;
|
||||||
y2 = y1;
|
y2 = y1;
|
||||||
y1 = y;
|
y1 = y;
|
||||||
|
@ -25,32 +26,26 @@ fn main() {
|
||||||
|
|
||||||
nested::magic_header();
|
nested::magic_header();
|
||||||
|
|
||||||
eprintln!(
|
eprintln!("
|
||||||
"
|
interface (Sequence ℕ) 0 1");
|
||||||
interface (Sequence ℕ) 0 1"
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut f0 = unsafe { File::from_raw_fd(0) };
|
let mut f0 = unsafe { File::from_raw_fd(0) };
|
||||||
eprintln!(
|
eprintln!("
|
||||||
"
|
|
||||||
>0: n
|
>0: n
|
||||||
( ℕ )
|
( ℕ )
|
||||||
( MachineInt )
|
( MachineInt )
|
||||||
( MachineWord )
|
( MachineWord )
|
||||||
( Stream MachineSyllab )
|
( Stream MachineSyllab )
|
||||||
"
|
");
|
||||||
);
|
|
||||||
|
|
||||||
let mut f1 = unsafe { File::from_raw_fd(1) };
|
let mut f1 = unsafe { File::from_raw_fd(1) };
|
||||||
eprintln!(
|
eprintln!("
|
||||||
"
|
|
||||||
<1: n'th fibonacci number
|
<1: n'th fibonacci number
|
||||||
( ℕ )
|
( ℕ )
|
||||||
( MachineInt )
|
( MachineInt )
|
||||||
( MachineWord )
|
( MachineWord )
|
||||||
( Stream MachineSyllab )
|
( Stream MachineSyllab )
|
||||||
"
|
");
|
||||||
);
|
|
||||||
|
|
||||||
nested::magic_header();
|
nested::magic_header();
|
||||||
|
|
||||||
|
@ -60,3 +55,4 @@ interface (Sequence ℕ) 0 1"
|
||||||
bytes = fib(n).to_le_bytes();
|
bytes = fib(n).to_le_bytes();
|
||||||
f1.write(&bytes).expect("");
|
f1.write(&bytes).expect("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
use std::{fs::File, io::Read, os::unix::io::FromRawFd};
|
|
||||||
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
io::{Read},
|
||||||
|
os::unix::io::FromRawFd
|
||||||
|
};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
nested::magic_header();
|
nested::magic_header();
|
||||||
|
@ -6,18 +11,15 @@ fn main() {
|
||||||
nested::magic_header();
|
nested::magic_header();
|
||||||
|
|
||||||
let mut f0 = unsafe { File::from_raw_fd(0) };
|
let mut f0 = unsafe { File::from_raw_fd(0) };
|
||||||
eprintln!(
|
eprintln!("
|
||||||
"
|
|
||||||
>0:
|
>0:
|
||||||
( ℕ )
|
( ℕ )
|
||||||
( MachineInt )
|
( MachineInt )
|
||||||
( MachineWord )
|
( MachineWord )
|
||||||
( Stream MachineSyllab )
|
( Stream MachineSyllab )
|
||||||
"
|
");
|
||||||
);
|
|
||||||
|
|
||||||
eprintln!(
|
eprintln!("
|
||||||
"
|
|
||||||
<1:
|
<1:
|
||||||
( ℕ )
|
( ℕ )
|
||||||
( PositionalInt 10 BigEndian )
|
( PositionalInt 10 BigEndian )
|
||||||
|
@ -25,8 +27,7 @@ fn main() {
|
||||||
( Sequence UTF-8-Char )
|
( Sequence UTF-8-Char )
|
||||||
( Stream UTF-8-Char )
|
( Stream UTF-8-Char )
|
||||||
( Stream MachineSyllab )
|
( Stream MachineSyllab )
|
||||||
"
|
");
|
||||||
);
|
|
||||||
|
|
||||||
nested::magic_header();
|
nested::magic_header();
|
||||||
|
|
||||||
|
@ -34,3 +35,4 @@ fn main() {
|
||||||
f0.read_exact(&mut bytes).expect("");
|
f0.read_exact(&mut bytes).expect("");
|
||||||
println!("{}", u64::from_le_bytes(bytes));
|
println!("{}", u64::from_le_bytes(bytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,68 +1,55 @@
|
||||||
use nested::{
|
|
||||||
core::{TypeDict, ViewPort},
|
use {
|
||||||
integer::RadixProjection,
|
nested::{
|
||||||
vec::VecBuffer,
|
core::{
|
||||||
|
ViewPort,
|
||||||
|
TypeDict
|
||||||
|
},
|
||||||
|
vec::{VecBuffer},
|
||||||
|
integer::{RadixProjection}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#[async_std::main]
|
#[async_std::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let mut td = TypeDict::new();
|
let mut td = TypeDict::new();
|
||||||
for tn in vec![
|
for tn in vec![
|
||||||
"MachineWord",
|
"MachineWord", "MachineInt", "MachineSyllab",
|
||||||
"MachineInt",
|
"Vec", "Stream", "Json",
|
||||||
"MachineSyllab",
|
"Sequence", "UTF-8-Char",
|
||||||
"Vec",
|
"PositionalInt", "Digit", "LittleEndian", "BigEndian",
|
||||||
"Stream",
|
"DiffStream", "ℕ",
|
||||||
"Json",
|
"$src_radix", "$dst_radix"
|
||||||
"Sequence",
|
] { td.add_typename(tn.into()); }
|
||||||
"UTF-8-Char",
|
|
||||||
"PositionalInt",
|
|
||||||
"Digit",
|
|
||||||
"LittleEndian",
|
|
||||||
"BigEndian",
|
|
||||||
"DiffStream",
|
|
||||||
"ℕ",
|
|
||||||
"$src_radix",
|
|
||||||
"$dst_radix",
|
|
||||||
] {
|
|
||||||
td.add_typename(tn.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let radix_types = vec![
|
let radix_types = vec![
|
||||||
td.type_term_from_str("( ℕ )").unwrap(),
|
td.type_term_from_str("( ℕ )").unwrap(),
|
||||||
td.type_term_from_str("( PositionalInt 10 LittleEndian )")
|
td.type_term_from_str("( PositionalInt 10 LittleEndian )").unwrap(),
|
||||||
.unwrap(),
|
|
||||||
td.type_term_from_str("( Sequence ( Digit 10 ) )").unwrap(),
|
td.type_term_from_str("( Sequence ( Digit 10 ) )").unwrap(),
|
||||||
td.type_term_from_str("( Sequence UTF-8-Char )").unwrap(),
|
td.type_term_from_str("( Sequence UTF-8-Char )").unwrap(),
|
||||||
td.type_term_from_str("( Sequence MachineSyllab )").unwrap(),
|
td.type_term_from_str("( Sequence MachineSyllab )").unwrap()
|
||||||
];
|
];
|
||||||
|
|
||||||
let src_types = vec![
|
let src_types = vec![
|
||||||
td.type_term_from_str("( ℕ )").unwrap(),
|
td.type_term_from_str("( ℕ )").unwrap(),
|
||||||
td.type_term_from_str("( PositionalInt $src_radix LittleEndian )")
|
td.type_term_from_str("( PositionalInt $src_radix LittleEndian )").unwrap(),
|
||||||
.unwrap(),
|
td.type_term_from_str("( Sequence ( Digit $src_radix ) )").unwrap(),
|
||||||
td.type_term_from_str("( Sequence ( Digit $src_radix ) )")
|
|
||||||
.unwrap(),
|
|
||||||
td.type_term_from_str("( Sequence MachineInt )").unwrap(),
|
td.type_term_from_str("( Sequence MachineInt )").unwrap(),
|
||||||
td.type_term_from_str("( DiffStream ( Vec MachineInt ) )")
|
td.type_term_from_str("( DiffStream ( Vec MachineInt ) )").unwrap(),
|
||||||
.unwrap(),
|
|
||||||
td.type_term_from_str("( Json )").unwrap(),
|
td.type_term_from_str("( Json )").unwrap(),
|
||||||
td.type_term_from_str("( Stream UTF-8-Char )").unwrap(),
|
td.type_term_from_str("( Stream UTF-8-Char )").unwrap(),
|
||||||
td.type_term_from_str("( Stream MachineSyllab )").unwrap(),
|
td.type_term_from_str("( Stream MachineSyllab )").unwrap()
|
||||||
];
|
];
|
||||||
|
|
||||||
let dst_types = vec![
|
let dst_types = vec![
|
||||||
td.type_term_from_str("( ℕ )").unwrap(),
|
td.type_term_from_str("( ℕ )").unwrap(),
|
||||||
td.type_term_from_str("( PositionalInt $dst_radix LittleEndian )")
|
td.type_term_from_str("( PositionalInt $dst_radix LittleEndian )").unwrap(),
|
||||||
.unwrap(),
|
td.type_term_from_str("( Sequence ( Digit $dst_radix ) )").unwrap(),
|
||||||
td.type_term_from_str("( Sequence ( Digit $dst_radix ) )")
|
|
||||||
.unwrap(),
|
|
||||||
td.type_term_from_str("( Sequence MachineInt )").unwrap(),
|
td.type_term_from_str("( Sequence MachineInt )").unwrap(),
|
||||||
td.type_term_from_str("( DiffStream ( Vec MachineInt ) )")
|
td.type_term_from_str("( DiffStream ( Vec MachineInt ) )").unwrap(),
|
||||||
.unwrap(),
|
|
||||||
td.type_term_from_str("( Json )").unwrap(),
|
td.type_term_from_str("( Json )").unwrap(),
|
||||||
td.type_term_from_str("( Stream UTF-8-Char )").unwrap(),
|
td.type_term_from_str("( Stream UTF-8-Char )").unwrap(),
|
||||||
td.type_term_from_str("( Stream MachineSyllab )").unwrap(),
|
td.type_term_from_str("( Stream MachineSyllab )").unwrap()
|
||||||
];
|
];
|
||||||
|
|
||||||
nested::magic_header();
|
nested::magic_header();
|
||||||
|
@ -112,26 +99,27 @@ async fn main() {
|
||||||
src_radix,
|
src_radix,
|
||||||
dst_radix,
|
dst_radix,
|
||||||
src_digits_port.outer().to_sequence(),
|
src_digits_port.outer().to_sequence(),
|
||||||
dst_digits_port.inner(),
|
dst_digits_port.inner()
|
||||||
);
|
);
|
||||||
|
|
||||||
// output dst digits
|
// output dst digits
|
||||||
let writer = {
|
let writer = {
|
||||||
use std::os::unix::io::FromRawFd;
|
use std::{
|
||||||
|
os::unix::io::FromRawFd
|
||||||
|
};
|
||||||
|
|
||||||
dst_digits_port
|
dst_digits_port.outer().serialize_json(unsafe { std::fs::File::from_raw_fd(1) })
|
||||||
.outer()
|
|
||||||
.serialize_json(unsafe { std::fs::File::from_raw_fd(1) })
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// start reading src digits
|
// start reading src digits
|
||||||
{
|
{
|
||||||
use async_std::os::unix::io::FromRawFd;
|
use async_std::{
|
||||||
|
os::unix::io::FromRawFd
|
||||||
|
};
|
||||||
|
|
||||||
src_digits
|
src_digits.from_json(unsafe { async_std::fs::File::from_raw_fd(0) }).await;
|
||||||
.from_json(unsafe { async_std::fs::File::from_raw_fd(0) })
|
|
||||||
.await;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
drop(writer);
|
drop(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{Read, Write},
|
io::{Read, Write},
|
||||||
os::unix::io::FromRawFd,
|
os::unix::io::FromRawFd
|
||||||
};
|
};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -9,19 +10,16 @@ fn main() {
|
||||||
eprintln!(" Parse MachineInt from String");
|
eprintln!(" Parse MachineInt from String");
|
||||||
nested::magic_header();
|
nested::magic_header();
|
||||||
|
|
||||||
eprintln!(
|
eprintln!("
|
||||||
"
|
|
||||||
$1: radix
|
$1: radix
|
||||||
( ℕ )
|
( ℕ )
|
||||||
( PositionalInt 10 BigEndian )
|
( PositionalInt 10 BigEndian )
|
||||||
( Sequence ( Digit 10 ) )
|
( Sequence ( Digit 10 ) )
|
||||||
( Sequence UTF-8-Char )
|
( Sequence UTF-8-Char )
|
||||||
( Sequence MachineSyllab )
|
( Sequence MachineSyllab )
|
||||||
"
|
");
|
||||||
);
|
|
||||||
|
|
||||||
eprintln!(
|
eprintln!("
|
||||||
"
|
|
||||||
>0: n
|
>0: n
|
||||||
( ℕ )
|
( ℕ )
|
||||||
( PositionalInt $radix BigEndian )
|
( PositionalInt $radix BigEndian )
|
||||||
|
@ -29,18 +27,15 @@ $1: radix
|
||||||
( Sequence UTF-8-Char )
|
( Sequence UTF-8-Char )
|
||||||
( Stream UTF-8-Char )
|
( Stream UTF-8-Char )
|
||||||
( Stream MachineSyllab )
|
( Stream MachineSyllab )
|
||||||
"
|
");
|
||||||
);
|
|
||||||
|
|
||||||
eprintln!(
|
eprintln!("
|
||||||
"
|
|
||||||
<1: n
|
<1: n
|
||||||
( ℕ )
|
( ℕ )
|
||||||
( MachineInt )
|
( MachineInt )
|
||||||
( MachineWord )
|
( MachineWord )
|
||||||
( Stream MachineSyllab )
|
( Stream MachineSyllab )
|
||||||
"
|
");
|
||||||
);
|
|
||||||
|
|
||||||
nested::magic_header();
|
nested::magic_header();
|
||||||
|
|
||||||
|
@ -60,10 +55,6 @@ $1: radix
|
||||||
let mut chars = Vec::new();
|
let mut chars = Vec::new();
|
||||||
f0.read_to_end(&mut chars).expect("");
|
f0.read_to_end(&mut chars).expect("");
|
||||||
chars.retain(|c| (*c as char).is_alphanumeric());
|
chars.retain(|c| (*c as char).is_alphanumeric());
|
||||||
f1.write(
|
f1.write(&u64::from_str_radix(&String::from_utf8_lossy(&chars), radix).unwrap().to_le_bytes()).expect("");
|
||||||
&u64::from_str_radix(&String::from_utf8_lossy(&chars), radix)
|
|
||||||
.unwrap()
|
|
||||||
.to_le_bytes(),
|
|
||||||
)
|
|
||||||
.expect("");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,12 +5,9 @@ name = "nested"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
#r3vi = { git = "https://git.exobiont.de/senvas/lib-r3vi.git" }
|
|
||||||
r3vi = { path = "../../lib-r3vi" }
|
|
||||||
laddertypes = { path = "../../lib-laddertypes" }
|
|
||||||
no_deadlocks = "*"
|
no_deadlocks = "*"
|
||||||
cgmath = { version = "0.18.0", features = ["serde"] }
|
cgmath = { version = "0.18.0", features = ["serde"] }
|
||||||
termion = "2.0.1"
|
termion = "1.5.5"
|
||||||
vte = "0.10.1"
|
vte = "0.10.1"
|
||||||
ansi_colours = "1.0"
|
ansi_colours = "1.0"
|
||||||
signal-hook = "0.3.1"
|
signal-hook = "0.3.1"
|
||||||
|
@ -19,6 +16,11 @@ serde = { version = "1.0", features = ["derive"] }
|
||||||
bincode = "1.3.3"
|
bincode = "1.3.3"
|
||||||
serde_json = "*"
|
serde_json = "*"
|
||||||
|
|
||||||
|
nako = {git= "https://git.exobiont.de/senvas/nako.git"}
|
||||||
|
nako_std = {git= "https://git.exobiont.de/senvas/nako.git"}
|
||||||
|
nakorender = {git="https://git.exobiont.de/senvas/nako.git", default-features = false}
|
||||||
|
font-kit = "*"
|
||||||
|
|
||||||
[dependencies.async-std]
|
[dependencies.async-std]
|
||||||
version = "1.9.0"
|
version = "1.9.0"
|
||||||
features = ["unstable", "attributes"]
|
features = ["unstable", "attributes"]
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
use std::{collections::HashMap, hash::Hash};
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
hash::Hash
|
||||||
|
};
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
pub struct Bimap<V: Eq + Hash, Λ: Eq + Hash> {
|
pub struct Bimap<V: Eq + Hash, Λ: Eq + Hash> {
|
||||||
pub mλ: HashMap<V, Λ>,
|
pub mλ: HashMap::<V, Λ>,
|
||||||
pub my: HashMap<Λ, V>,
|
pub my: HashMap::<Λ, V>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: Eq + Hash + Clone, Λ: Eq + Hash + Clone> Bimap<V, Λ> {
|
impl<V: Eq + Hash + Clone, Λ: Eq + Hash + Clone> Bimap<V, Λ> {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Bimap {
|
Bimap {
|
||||||
mλ: HashMap::new(),
|
mλ: HashMap::new(),
|
||||||
my: HashMap::new(),
|
my: HashMap::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,3 +25,4 @@ impl<V: Eq + Hash + Clone, Λ: Eq + Hash + Clone> Bimap<V, Λ> {
|
||||||
}
|
}
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
|
|
||||||
pub trait Commander {
|
|
||||||
type Cmd;
|
|
||||||
|
|
||||||
fn send_cmd(&mut self, cmd: &Self::Cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
use std::sync::{Arc, RwLock};
|
|
||||||
use crate::{
|
|
||||||
type_system::ReprTree,
|
|
||||||
tree::{nav::TreeNavResult}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub trait ObjCommander {
|
|
||||||
fn send_cmd_obj(&mut self, cmd_obj: Arc<RwLock<ReprTree>>) -> TreeNavResult;
|
|
||||||
}
|
|
||||||
|
|
221
nested/src/core/channel.rs
Normal file
221
nested/src/core/channel.rs
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
use {
|
||||||
|
core::{
|
||||||
|
task::{Poll, Context, Waker},
|
||||||
|
pin::Pin
|
||||||
|
},
|
||||||
|
std::{
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
collections::HashSet,
|
||||||
|
hash::Hash
|
||||||
|
},
|
||||||
|
async_std::{
|
||||||
|
stream::Stream
|
||||||
|
},
|
||||||
|
|
||||||
|
crate::{
|
||||||
|
core::{Observer}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*\
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
Traits
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
\*/
|
||||||
|
pub trait ChannelData : Default + IntoIterator + Send + Sync {
|
||||||
|
fn channel_insert(&mut self, x: Self::Item);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*\
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
Queue Channel
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
\*/
|
||||||
|
impl<T> ChannelData for Vec<T>
|
||||||
|
where T: Send + Sync {
|
||||||
|
fn channel_insert(&mut self, x: T) {
|
||||||
|
self.push(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*\
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
Set Channel
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
\*/
|
||||||
|
impl<T> ChannelData for HashSet<T>
|
||||||
|
where T: Eq + Hash + Send + Sync {
|
||||||
|
fn channel_insert(&mut self, x: T) {
|
||||||
|
self.insert(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*\
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
Singleton Channel
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
\*/
|
||||||
|
impl<T> ChannelData for Option<T>
|
||||||
|
where T: Send + Sync {
|
||||||
|
fn channel_insert(&mut self, x: T) {
|
||||||
|
*self = Some(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*\
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
Channel
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
\*/
|
||||||
|
struct ChannelState<Data: ChannelData> {
|
||||||
|
send_buf: Option<Data>,
|
||||||
|
recv_iter: Option<Data::IntoIter>,
|
||||||
|
num_senders: usize,
|
||||||
|
waker: Option<Waker>
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub struct ChannelSender<Data: ChannelData>(Arc<Mutex<ChannelState<Data>>>);
|
||||||
|
pub struct ChannelReceiver<Data: ChannelData>(Arc<Mutex<ChannelState<Data>>>);
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<Data: ChannelData> ChannelSender<Data>
|
||||||
|
where Data::IntoIter: Send + Sync {
|
||||||
|
pub fn send(&self, msg: Data::Item) {
|
||||||
|
let mut state = self.0.lock().unwrap();
|
||||||
|
|
||||||
|
if state.send_buf.is_none() {
|
||||||
|
state.send_buf = Some(Data::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
state.send_buf.as_mut().unwrap().channel_insert(msg);
|
||||||
|
|
||||||
|
if let Some(waker) = state.waker.take() {
|
||||||
|
waker.wake();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use crate::core::View;
|
||||||
|
impl<V: View + ?Sized, Data: ChannelData<Item = V::Msg>> Observer<V> for ChannelSender<Data>
|
||||||
|
where V::Msg: Clone, Data::IntoIter: Send + Sync {
|
||||||
|
fn notify(&mut self, msg: &V::Msg) {
|
||||||
|
self.send(msg.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Data: ChannelData> Clone for ChannelSender<Data> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
self.0.lock().unwrap().num_senders += 1;
|
||||||
|
ChannelSender(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Data: ChannelData> Drop for ChannelSender<Data> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let mut state = self.0.lock().unwrap();
|
||||||
|
state.num_senders -= 1;
|
||||||
|
if let Some(waker) = state.waker.take() {
|
||||||
|
waker.wake();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<Data: ChannelData> ChannelReceiver<Data> {
|
||||||
|
pub async fn recv(&self) -> Option<Data> {
|
||||||
|
ChannelRead(self.0.clone()).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_recv(&self) -> Option<Data> {
|
||||||
|
let mut state = self.0.lock().unwrap();
|
||||||
|
if let Some(buf) = state.send_buf.take() {
|
||||||
|
Some(buf)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ChannelRead<Data: ChannelData>(Arc<Mutex<ChannelState<Data>>>);
|
||||||
|
impl<Data: ChannelData> std::future::Future for ChannelRead<Data> {
|
||||||
|
type Output = Option<Data>;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
||||||
|
let mut state = self.0.lock().unwrap();
|
||||||
|
if let Some(buf) = state.send_buf.take() {
|
||||||
|
Poll::Ready(Some(buf))
|
||||||
|
} else if state.num_senders == 0 {
|
||||||
|
Poll::Ready(None)
|
||||||
|
} else {
|
||||||
|
state.waker = Some(cx.waker().clone());
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<Data: ChannelData> Stream for ChannelReceiver<Data> {
|
||||||
|
type Item = Data::Item;
|
||||||
|
|
||||||
|
fn poll_next(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>
|
||||||
|
) -> Poll<Option<Self::Item>> {
|
||||||
|
let mut state = self.0.lock().unwrap();
|
||||||
|
|
||||||
|
if let Some(recv_iter) = state.recv_iter.as_mut() {
|
||||||
|
if let Some(val) = recv_iter.next() {
|
||||||
|
return Poll::Ready(Some(val))
|
||||||
|
} else {
|
||||||
|
state.recv_iter = None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(send_buf) = state.send_buf.take() {
|
||||||
|
state.recv_iter = Some(send_buf.into_iter());
|
||||||
|
// recv_iter.next() is guaranteed to be Some(x)
|
||||||
|
Poll::Ready(state.recv_iter.as_mut().unwrap().next())
|
||||||
|
} else if state.num_senders == 0 {
|
||||||
|
Poll::Ready(None)
|
||||||
|
} else {
|
||||||
|
state.waker = Some(cx.waker().clone());
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*\
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
Factory Functions
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
\*/
|
||||||
|
pub fn channel<Data: ChannelData>() -> (ChannelSender<Data>, ChannelReceiver<Data>) {
|
||||||
|
let state = Arc::new(Mutex::new(ChannelState{
|
||||||
|
send_buf: None,
|
||||||
|
recv_iter: None,
|
||||||
|
num_senders: 1,
|
||||||
|
waker: None
|
||||||
|
}));
|
||||||
|
|
||||||
|
(ChannelSender(state.clone()), ChannelReceiver(state))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_channel<T: Eq + Hash + Send + Sync>() -> (ChannelSender<HashSet<T>>, ChannelReceiver<HashSet<T>>) {
|
||||||
|
channel::<HashSet<T>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn queue_channel<T: Send + Sync>() -> (ChannelSender<Vec<T>>, ChannelReceiver<Vec<T>>) {
|
||||||
|
channel::<Vec<T>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn singleton_channel<T: Send + Sync>() -> (ChannelSender<Option<T>>, ChannelReceiver<Option<T>>) {
|
||||||
|
channel::<Option<T>>()
|
||||||
|
}
|
||||||
|
|
394
nested/src/core/context.rs
Normal file
394
nested/src/core/context.rs
Normal file
|
@ -0,0 +1,394 @@
|
||||||
|
use {
|
||||||
|
std::{
|
||||||
|
collections::HashMap,
|
||||||
|
sync::{Arc, RwLock}
|
||||||
|
},
|
||||||
|
crate::{
|
||||||
|
core::{
|
||||||
|
type_term::{
|
||||||
|
TypeTerm,
|
||||||
|
TypeDict
|
||||||
|
},
|
||||||
|
View,
|
||||||
|
OuterViewPort,
|
||||||
|
AnyOuterViewPort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ReprTree {
|
||||||
|
port: Option<AnyOuterViewPort>,
|
||||||
|
branches: HashMap<TypeTerm, Arc<RwLock<ReprTree>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReprTree {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
ReprTree {
|
||||||
|
port: None,
|
||||||
|
branches: HashMap::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_leaf(
|
||||||
|
port: AnyOuterViewPort
|
||||||
|
) -> Arc<RwLock<Self>> {
|
||||||
|
let mut tree = ReprTree::new();
|
||||||
|
tree.insert_leaf(vec![].into_iter(), port);
|
||||||
|
Arc::new(RwLock::new(tree))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_branch(
|
||||||
|
&mut self,
|
||||||
|
type_tag: TypeTerm,
|
||||||
|
repr: Arc<RwLock<ReprTree>>
|
||||||
|
) {
|
||||||
|
self.branches.insert(type_tag, repr);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_leaf(
|
||||||
|
&mut self,
|
||||||
|
mut type_ladder: impl Iterator<Item = TypeTerm>,
|
||||||
|
port: AnyOuterViewPort
|
||||||
|
) {
|
||||||
|
if let Some(type_term) = type_ladder.next() {
|
||||||
|
if let Some(next_repr) = self.branches.get(&type_term) {
|
||||||
|
next_repr.write().unwrap().insert_leaf(type_ladder, port);
|
||||||
|
} else {
|
||||||
|
let mut next_repr = ReprTree::new();
|
||||||
|
next_repr.insert_leaf(type_ladder, port);
|
||||||
|
self.insert_branch(type_term, Arc::new(RwLock::new(next_repr)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.port = Some(port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Object {
|
||||||
|
pub type_tag: TypeTerm,
|
||||||
|
pub repr: Arc<RwLock<ReprTree>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Object {
|
||||||
|
pub fn get_port<V: View + ?Sized + 'static>(&self) -> Option<OuterViewPort<V>> where V::Msg: Clone {
|
||||||
|
Some(self.repr.read().unwrap().port.clone()?.downcast::<V>().ok().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn downcast(&self, dst_type: TypeTerm) -> Option<Object> {
|
||||||
|
if let Some(repr) = self.repr.read().unwrap().branches.get(&dst_type) {
|
||||||
|
Some(Object {
|
||||||
|
type_tag: dst_type,
|
||||||
|
repr: repr.clone()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn downcast_ladder(
|
||||||
|
&self,
|
||||||
|
repr_ladder: impl Iterator<Item = TypeTerm>
|
||||||
|
) -> Option<Object> {
|
||||||
|
repr_ladder.fold(
|
||||||
|
Some(self.clone()),
|
||||||
|
|s, t| s?.downcast(t.clone())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_iso_repr(
|
||||||
|
&self,
|
||||||
|
type_ladder: impl Iterator<Item = TypeTerm>,
|
||||||
|
morphism_constructors: &HashMap<MorphismType, Box<dyn Fn(Object) -> Object>>
|
||||||
|
) {
|
||||||
|
let mut cur_repr = self.repr.clone();
|
||||||
|
|
||||||
|
for dst_type in type_ladder {
|
||||||
|
if let Some(next_repr) = self.repr.read().unwrap().branches.get(&dst_type) {
|
||||||
|
// go deeper
|
||||||
|
cur_repr = next_repr.clone();
|
||||||
|
} else {
|
||||||
|
// search for morphism constructor and insert new repr
|
||||||
|
let mut obj = None;
|
||||||
|
|
||||||
|
for src_type in cur_repr.read().unwrap().branches.keys() {
|
||||||
|
if let Some(ctor) = morphism_constructors.get(
|
||||||
|
&MorphismType {
|
||||||
|
mode: MorphismMode::Iso,
|
||||||
|
src_type: src_type.clone(),
|
||||||
|
dst_type: dst_type.clone()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
let new_obj = ctor(
|
||||||
|
Object {
|
||||||
|
type_tag: src_type.clone(),
|
||||||
|
repr: cur_repr.read().unwrap()
|
||||||
|
.branches
|
||||||
|
.get(&src_type).unwrap().clone()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(new_obj.type_tag == dst_type);
|
||||||
|
|
||||||
|
obj = Some(new_obj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(obj) = obj {
|
||||||
|
cur_repr.write().unwrap().insert_branch(obj.type_tag, obj.repr);
|
||||||
|
} else {
|
||||||
|
panic!("could not find matching isomorphism!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_mono_repr<'a>(
|
||||||
|
&self,
|
||||||
|
type_ladder: impl Iterator<Item = TypeTerm>,
|
||||||
|
morphism_constructors: &HashMap<MorphismType, Box<dyn Fn(Object) -> Object>>
|
||||||
|
) {
|
||||||
|
let mut cur_type = self.type_tag.clone();
|
||||||
|
let mut cur_repr = self.repr.clone();
|
||||||
|
|
||||||
|
for dst_type in type_ladder {
|
||||||
|
if let Some(next_repr) = self.repr.read().unwrap().branches.get(&dst_type) {
|
||||||
|
// go deeper
|
||||||
|
cur_type = dst_type;
|
||||||
|
cur_repr = next_repr.clone();
|
||||||
|
} else {
|
||||||
|
if let Some(constructor) = morphism_constructors.get(
|
||||||
|
&MorphismType {
|
||||||
|
mode: MorphismMode::Mono,
|
||||||
|
src_type: cur_type.clone(),
|
||||||
|
dst_type: dst_type.clone()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
let new_obj = constructor(
|
||||||
|
Object {
|
||||||
|
type_tag: cur_type.clone(),
|
||||||
|
repr: cur_repr.read().unwrap()
|
||||||
|
.branches
|
||||||
|
.get(&cur_type).unwrap().clone()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(new_obj.type_tag == dst_type);
|
||||||
|
cur_repr.write().unwrap().insert_branch(new_obj.type_tag.clone(), new_obj.repr.clone());
|
||||||
|
|
||||||
|
cur_type = new_obj.type_tag;
|
||||||
|
cur_repr = new_obj.repr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace with higher-level type in which self is a repr branch
|
||||||
|
pub fn epi_cast<'a>(
|
||||||
|
&self,
|
||||||
|
_type_ladder: impl Iterator<Item = TypeTerm>,
|
||||||
|
_morphism_constructors: &HashMap<MorphismType, Box<dyn Fn(Object) -> Object>>
|
||||||
|
) {
|
||||||
|
// todo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub struct TypeLadder(Vec<TypeTerm>);
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
||||||
|
pub enum MorphismMode {
|
||||||
|
/// Isomorphism
|
||||||
|
/// e.g. `( PositionalInteger 10 BigEndian ) <~> ( PositionalInteger 16 LittleEndian )`
|
||||||
|
Iso,
|
||||||
|
|
||||||
|
/// Monomorphism, i.e. injective functions,
|
||||||
|
/// upcast-view, downcast-control, semantic gain
|
||||||
|
/// e.g. `( Sequence ( Digit 16 ) ) ~> ( PositionalInteger 16 LittleEndian )`
|
||||||
|
Mono,
|
||||||
|
|
||||||
|
/// Epimorphsim, i.e. surjective functions,
|
||||||
|
/// upcast-control, downcast-view, possible loss of entropy
|
||||||
|
/// e.g. `( Ascii ) ~> ( Digit 16 )`
|
||||||
|
Epi,
|
||||||
|
|
||||||
|
/// Any other function
|
||||||
|
Any
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub struct MorphismType {
|
||||||
|
pub mode: MorphismMode,
|
||||||
|
pub src_type: TypeTerm,
|
||||||
|
pub dst_type: TypeTerm
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub struct Context {
|
||||||
|
type_dict: TypeDict,
|
||||||
|
default_constructors: HashMap<TypeTerm, Box<dyn Fn() -> Object + Send + Sync>>,
|
||||||
|
morphism_constructors: HashMap<MorphismType, Box<dyn Fn(Object) -> Object + Send + Sync>>,
|
||||||
|
objects: HashMap<String, Object>,
|
||||||
|
parent: Option<Arc<RwLock<Context>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
|
pub fn with_parent(parent: Option<Arc<RwLock<Context>>>) -> Self {
|
||||||
|
Context {
|
||||||
|
type_dict: TypeDict::new(),
|
||||||
|
default_constructors: HashMap::new(),
|
||||||
|
morphism_constructors: HashMap::new(),
|
||||||
|
objects: HashMap::new(),
|
||||||
|
parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Context::with_parent(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_typename(&mut self, tn: String) {
|
||||||
|
self.type_dict.add_typename(tn);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn type_term_from_str(&self, tn: &str) -> Option<TypeTerm> {
|
||||||
|
self.type_dict.type_term_from_str(&tn)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_morphism(
|
||||||
|
&mut self,
|
||||||
|
morph_type: MorphismType,
|
||||||
|
morph_fn: Box<dyn Fn(Object) -> Object + Send + Sync>
|
||||||
|
) {
|
||||||
|
self.morphism_constructors.insert(morph_type, morph_fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// adds an object without any representations
|
||||||
|
pub fn add_obj(
|
||||||
|
&mut self,
|
||||||
|
name: String,
|
||||||
|
typename: &str
|
||||||
|
) {
|
||||||
|
let type_tag = self.type_dict.type_term_from_str(typename).unwrap();
|
||||||
|
|
||||||
|
self.objects.insert(
|
||||||
|
name,
|
||||||
|
if let Some(ctor) = self.default_constructors.get(&type_tag) {
|
||||||
|
ctor()
|
||||||
|
} else {
|
||||||
|
Object {
|
||||||
|
type_tag,
|
||||||
|
repr: Arc::new(RwLock::new(ReprTree::new()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_obj(
|
||||||
|
&self,
|
||||||
|
name: &String
|
||||||
|
) -> Option<Object> {
|
||||||
|
if let Some(obj) = self.objects.get(name) {
|
||||||
|
Some(obj.clone())
|
||||||
|
} else if let Some(parent) = self.parent.as_ref() {
|
||||||
|
parent.read().unwrap().get_obj(name)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_obj_port<
|
||||||
|
'a,
|
||||||
|
V: View + ?Sized + 'static
|
||||||
|
>(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
type_ladder: impl Iterator<Item = &'a str>
|
||||||
|
) -> Option<OuterViewPort<V>>
|
||||||
|
where V::Msg: Clone {
|
||||||
|
self.get_obj(&name.into())?
|
||||||
|
.downcast_ladder(
|
||||||
|
type_ladder.map(|tn| self.type_dict.type_term_from_str(tn).unwrap())
|
||||||
|
)?
|
||||||
|
.get_port()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_repr<'a>(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
type_ladder: impl Iterator<Item = &'a str>,
|
||||||
|
port: AnyOuterViewPort
|
||||||
|
) {
|
||||||
|
self.get_obj(&name.to_string()).unwrap()
|
||||||
|
.repr.write().unwrap()
|
||||||
|
.insert_leaf(
|
||||||
|
type_ladder.map(|tn| self.type_dict.type_term_from_str(tn).unwrap()),
|
||||||
|
port
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn epi_cast(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
typename: &str
|
||||||
|
) {
|
||||||
|
let dst_type = self.type_dict.type_term_from_str(typename).unwrap();
|
||||||
|
let old_obj = self.objects.get(&name.to_string()).unwrap().clone();
|
||||||
|
let new_obj =
|
||||||
|
if let Some(ctor) = self.morphism_constructors.get(
|
||||||
|
&MorphismType {
|
||||||
|
mode: MorphismMode::Epi,
|
||||||
|
src_type: old_obj.type_tag.clone(),
|
||||||
|
dst_type: dst_type.clone()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
ctor(old_obj.clone())
|
||||||
|
} else {
|
||||||
|
Object {
|
||||||
|
type_tag: dst_type,
|
||||||
|
repr: Arc::new(RwLock::new(ReprTree::new()))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
new_obj.repr.write().unwrap().insert_branch(
|
||||||
|
old_obj.type_tag,
|
||||||
|
old_obj.repr
|
||||||
|
);
|
||||||
|
|
||||||
|
self.objects.insert(name.to_string(), new_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mono_view<'a, V: View + ?Sized + 'static>(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
type_ladder: impl Iterator<Item = &'a str>
|
||||||
|
) -> Option<OuterViewPort<V>>
|
||||||
|
where V::Msg: Clone {
|
||||||
|
if let Some(p) = self.get_obj_port(name, type_ladder) {
|
||||||
|
Some(p)
|
||||||
|
} else {
|
||||||
|
// todo : add repr with morphism constructor (if one exists)
|
||||||
|
/*
|
||||||
|
if let Some(ctor) = self.morphism_constructors.get(
|
||||||
|
&MorphismType {
|
||||||
|
mode: MorphismMode::Mono,
|
||||||
|
src_type: old_obj.type_tag.clone(),
|
||||||
|
dst_type:
|
||||||
|
}
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
47
nested/src/core/mod.rs
Normal file
47
nested/src/core/mod.rs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
|
||||||
|
pub mod view;
|
||||||
|
pub mod observer;
|
||||||
|
pub mod channel;
|
||||||
|
pub mod port;
|
||||||
|
pub mod type_term;
|
||||||
|
pub mod context;
|
||||||
|
|
||||||
|
pub use {
|
||||||
|
view::{View},
|
||||||
|
observer::{
|
||||||
|
Observer,
|
||||||
|
ObserverExt,
|
||||||
|
ObserverBroadcast,
|
||||||
|
NotifyFnObserver,
|
||||||
|
ResetFnObserver
|
||||||
|
},
|
||||||
|
channel::{
|
||||||
|
ChannelReceiver,
|
||||||
|
ChannelSender,
|
||||||
|
set_channel,
|
||||||
|
queue_channel,
|
||||||
|
singleton_channel
|
||||||
|
},
|
||||||
|
port::{
|
||||||
|
ViewPort,
|
||||||
|
InnerViewPort,
|
||||||
|
OuterViewPort,
|
||||||
|
AnyViewPort,
|
||||||
|
AnyOuterViewPort,
|
||||||
|
AnyInnerViewPort,
|
||||||
|
},
|
||||||
|
type_term::{
|
||||||
|
TypeID,
|
||||||
|
TypeTerm,
|
||||||
|
TypeDict,
|
||||||
|
},
|
||||||
|
context::{
|
||||||
|
ReprTree,
|
||||||
|
Object,
|
||||||
|
Context,
|
||||||
|
MorphismMode,
|
||||||
|
MorphismType
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
166
nested/src/core/observer.rs
Normal file
166
nested/src/core/observer.rs
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
core::{
|
||||||
|
View,
|
||||||
|
channel::{channel, ChannelSender, ChannelReceiver}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
std::{
|
||||||
|
sync::{Arc, Weak}
|
||||||
|
},
|
||||||
|
std::sync::RwLock
|
||||||
|
};
|
||||||
|
|
||||||
|
/*\
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
Observer
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
\*/
|
||||||
|
pub trait Observer<V: View + ?Sized> : Send + Sync {
|
||||||
|
fn reset(&mut self, _view: Option<Arc<V>>) {}
|
||||||
|
fn notify(&mut self, msg: &V::Msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<V: View + ?Sized, O: Observer<V>> Observer<V> for Arc<RwLock<O>> {
|
||||||
|
fn reset(&mut self, view: Option<Arc<V>>) {
|
||||||
|
self.write().unwrap().reset(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&mut self, msg: &V::Msg) {
|
||||||
|
self.write().unwrap().notify(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub trait ObserverExt<V: View + ?Sized> : Observer<V> {
|
||||||
|
fn notify_each(&mut self, it: impl IntoIterator<Item = V::Msg>);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View + ?Sized, T: Observer<V>> ObserverExt<V> for T {
|
||||||
|
fn notify_each(&mut self, it: impl IntoIterator<Item = V::Msg>) {
|
||||||
|
for msg in it {
|
||||||
|
self.notify(&msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*\
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
Broadcast
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
\*/
|
||||||
|
pub struct ObserverBroadcast<V: View + ?Sized>
|
||||||
|
where V::Msg : Send + Sync {
|
||||||
|
rx: ChannelReceiver<Vec<V::Msg>>,
|
||||||
|
tx: ChannelSender<Vec<V::Msg>>,
|
||||||
|
observers: Vec<Weak<RwLock<dyn Observer<V>>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View + ?Sized> ObserverBroadcast<V>
|
||||||
|
where V::Msg : Clone + Send + Sync {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let (tx, rx) = channel::<Vec<V::Msg>>();
|
||||||
|
ObserverBroadcast {
|
||||||
|
rx, tx,
|
||||||
|
observers: Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_observer(&mut self, obs: Weak<RwLock<dyn Observer<V>>>) {
|
||||||
|
self.cleanup();
|
||||||
|
self.observers.push(obs);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cleanup(&mut self) {
|
||||||
|
self.observers.retain(|o| o.strong_count() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn iter(&self) -> impl Iterator<Item = Arc<RwLock<dyn Observer<V>>>> + '_ {
|
||||||
|
self.observers.iter().filter_map(|o| o.upgrade())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&self) {
|
||||||
|
if let Some(msg_vec) = self.rx.try_recv() {
|
||||||
|
for msg in msg_vec {
|
||||||
|
for o in self.iter() {
|
||||||
|
o.write().unwrap().notify(&msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View + ?Sized> Observer<V> for ObserverBroadcast<V>
|
||||||
|
where V::Msg: Clone {
|
||||||
|
fn reset(&mut self, view: Option<Arc<V>>) {
|
||||||
|
for o in self.iter() {
|
||||||
|
o.write().unwrap().reset(view.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&mut self, msg: &V::Msg) {
|
||||||
|
self.tx.send(msg.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub struct NotifyFnObserver<V, F>
|
||||||
|
where V: View + ?Sized,
|
||||||
|
F: Fn(&V::Msg) + Send + Sync {
|
||||||
|
f: F,
|
||||||
|
_phantom: std::marker::PhantomData<V>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V, F> NotifyFnObserver<V, F>
|
||||||
|
where V: View + ?Sized,
|
||||||
|
F: Fn(&V::Msg) + Send + Sync {
|
||||||
|
pub fn new(f: F) -> Self {
|
||||||
|
NotifyFnObserver {
|
||||||
|
f,
|
||||||
|
_phantom: std::marker::PhantomData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V, F> Observer<V> for NotifyFnObserver<V, F>
|
||||||
|
where V: View + ?Sized,
|
||||||
|
F: Fn(&V::Msg) + Send + Sync {
|
||||||
|
fn notify(&mut self, msg: &V::Msg) {
|
||||||
|
(self.f)(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub struct ResetFnObserver<V, F>
|
||||||
|
where V: View + ?Sized,
|
||||||
|
F: Fn(Option<Arc<V>>) + Send + Sync {
|
||||||
|
f: F,
|
||||||
|
_phantom: std::marker::PhantomData<V>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V, F> ResetFnObserver<V, F>
|
||||||
|
where V: View + ?Sized,
|
||||||
|
F: Fn(Option<Arc<V>>) + Send + Sync {
|
||||||
|
pub fn new(f: F) -> Self {
|
||||||
|
ResetFnObserver {
|
||||||
|
f,
|
||||||
|
_phantom: std::marker::PhantomData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V, F> Observer<V> for ResetFnObserver<V, F>
|
||||||
|
where V: View + ?Sized,
|
||||||
|
F: Fn(Option<Arc<V>>) + Send + Sync {
|
||||||
|
fn notify(&mut self, _msg: &V::Msg) {}
|
||||||
|
fn reset(&mut self, view: Option<Arc<V>>) {
|
||||||
|
(self.f)(view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
295
nested/src/core/port.rs
Normal file
295
nested/src/core/port.rs
Normal file
|
@ -0,0 +1,295 @@
|
||||||
|
use {
|
||||||
|
std::sync::Arc,
|
||||||
|
std::any::Any,
|
||||||
|
std::sync::RwLock,
|
||||||
|
crate::core::{
|
||||||
|
View,
|
||||||
|
Observer,
|
||||||
|
ObserverBroadcast,
|
||||||
|
NotifyFnObserver,
|
||||||
|
ResetFnObserver
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
pub trait UpdateTask : Send + Sync {
|
||||||
|
fn update(&self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*\
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
View Port
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
\*/
|
||||||
|
pub struct ViewPort<V: View + ?Sized> {
|
||||||
|
view: Arc<RwLock<Option<Arc<V>>>>,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<V>>>,
|
||||||
|
pub update_hooks: Arc<RwLock<Vec<Arc<dyn UpdateTask>>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View + ?Sized> ViewPort<V>
|
||||||
|
where V::Msg: Clone {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
ViewPort {
|
||||||
|
view: Arc::new(RwLock::new(None)),
|
||||||
|
cast: Arc::new(RwLock::new(ObserverBroadcast::new())),
|
||||||
|
update_hooks: Arc::new(RwLock::new(Vec::new()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_view(view: Arc<V>) -> Self {
|
||||||
|
let port = ViewPort::new();
|
||||||
|
port.set_view(Some(view));
|
||||||
|
port
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_view(&self, view: Option<Arc<V>>) {
|
||||||
|
self.update();
|
||||||
|
*self.view.write().unwrap() = view.clone();
|
||||||
|
self.cast.write().unwrap().reset(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_observer(&self, observer: Arc<RwLock<dyn Observer<V>>>) {
|
||||||
|
self.update();
|
||||||
|
self.cast.write().unwrap().add_observer(Arc::downgrade(&observer));
|
||||||
|
observer.write().unwrap().reset(self.view.read().unwrap().clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_update_hook(&self, hook_cast: Arc<dyn UpdateTask>) {
|
||||||
|
self.update_hooks.write().unwrap().push(hook_cast);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inner(&self) -> InnerViewPort<V> {
|
||||||
|
InnerViewPort(
|
||||||
|
ViewPort{
|
||||||
|
view: self.view.clone(),
|
||||||
|
cast: self.cast.clone(),
|
||||||
|
update_hooks: self.update_hooks.clone()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn outer(&self) -> OuterViewPort<V> {
|
||||||
|
OuterViewPort(
|
||||||
|
ViewPort{
|
||||||
|
view: self.view.clone(),
|
||||||
|
cast: self.cast.clone(),
|
||||||
|
update_hooks: self.update_hooks.clone()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_inner(self) -> InnerViewPort<V> {
|
||||||
|
InnerViewPort(
|
||||||
|
ViewPort{
|
||||||
|
view: self.view,
|
||||||
|
cast: self.cast,
|
||||||
|
update_hooks: self.update_hooks
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_outer(self) -> OuterViewPort<V> {
|
||||||
|
OuterViewPort(
|
||||||
|
ViewPort{
|
||||||
|
view: self.view,
|
||||||
|
cast: self.cast,
|
||||||
|
update_hooks: self.update_hooks
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View + ?Sized> UpdateTask for ViewPort<V> where V::Msg: Clone + Send + Sync{
|
||||||
|
fn update(&self) {
|
||||||
|
let v = {
|
||||||
|
let t = self.update_hooks.read().unwrap();
|
||||||
|
t.iter().cloned().collect::<Vec<_>>()
|
||||||
|
};
|
||||||
|
|
||||||
|
for hook in v {
|
||||||
|
hook.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cast.read().unwrap().update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<V: View + ?Sized> Clone for ViewPort<V>
|
||||||
|
where V::Msg: Clone {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
ViewPort {
|
||||||
|
view: self.view.clone(),
|
||||||
|
cast: self.cast.clone(),
|
||||||
|
update_hooks: self.update_hooks.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub struct InnerViewPort<V: View + ?Sized>(pub ViewPort<V>) where V::Msg: Clone;
|
||||||
|
pub struct OuterViewPort<V: View + ?Sized>(pub ViewPort<V>) where V::Msg: Clone;
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<V: View + ?Sized> InnerViewPort<V> where V::Msg: Clone {
|
||||||
|
pub fn get_broadcast(&self) -> Arc<RwLock<ObserverBroadcast<V>>> {
|
||||||
|
self.0.cast.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_view(&self, view: Option<Arc<V>>) -> Arc<RwLock<ObserverBroadcast<V>>> {
|
||||||
|
self.0.set_view(view);
|
||||||
|
self.get_broadcast()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_view(&self) -> Option<Arc<V>> {
|
||||||
|
self.0.view.read().unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn notify(&self, msg: &V::Msg) {
|
||||||
|
self.0.cast.write().unwrap().notify(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View + ?Sized> Clone for InnerViewPort<V>
|
||||||
|
where V::Msg: Clone {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
InnerViewPort(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<V: View + ?Sized + 'static> OuterViewPort<V>
|
||||||
|
where V::Msg: Clone {
|
||||||
|
pub fn get_view(&self) -> Option<Arc<V>> {
|
||||||
|
self.0.view.read().unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_view_arc(&self) -> Arc<RwLock<Option<Arc<V>>>> {
|
||||||
|
self.0.view.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_observer(&self, observer: Arc<RwLock<dyn Observer<V>>>) -> Arc<RwLock<Option<Arc<V>>>> {
|
||||||
|
self.0.add_observer(observer);
|
||||||
|
self.get_view_arc()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_reset_fn<F: Fn(Option<Arc<V>>) + Send + Sync + 'static>(&self, reset: F) -> Arc<RwLock<ResetFnObserver<V, F>>> {
|
||||||
|
let obs = Arc::new(RwLock::new(ResetFnObserver::new(reset)));
|
||||||
|
self.add_observer(obs.clone());
|
||||||
|
obs
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_notify_fn<F: Fn(&V::Msg) + Send + Sync + 'static>(&self, notify: F) -> Arc<RwLock<NotifyFnObserver<V, F>>> {
|
||||||
|
let obs = Arc::new(RwLock::new(NotifyFnObserver::new(notify)));
|
||||||
|
self.add_observer(obs.clone());
|
||||||
|
obs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View + ?Sized> Clone for OuterViewPort<V>
|
||||||
|
where V::Msg: Clone {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
OuterViewPort(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
impl<V: View + ?Sized + 'static> OuterViewPort<V>
|
||||||
|
where V::Msg: Clone {
|
||||||
|
pub fn into_stream<Data>(
|
||||||
|
self,
|
||||||
|
reset: impl Fn(Option<Arc<V>>, ChannelSender<Data>) + Send + Sync + 'static
|
||||||
|
) -> ChannelReceiver<Data>
|
||||||
|
where Data: ChannelData<Item = V::Msg> + 'static,
|
||||||
|
Data::IntoIter: Send + Sync + 'static
|
||||||
|
{
|
||||||
|
let (s, r) = crate::core::channel::channel::<Data>();
|
||||||
|
self.add_observer(Arc::new(s.clone()));
|
||||||
|
self.add_reset_fn(
|
||||||
|
move |view| { reset(view, s.clone()); }
|
||||||
|
);
|
||||||
|
r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AnyViewPort {
|
||||||
|
view: Arc<dyn Any + Send + Sync + 'static>,
|
||||||
|
cast: Arc<dyn Any + Send + Sync + 'static>,
|
||||||
|
update_hooks: Arc<RwLock<Vec<Arc<dyn UpdateTask>>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnyViewPort {
|
||||||
|
pub fn downcast<V: View + ?Sized + 'static>(self) -> Result<ViewPort<V>, AnyViewPort> {
|
||||||
|
match (
|
||||||
|
self.view.clone().downcast::<RwLock<Option<Arc<V>>>>(),
|
||||||
|
self.cast.clone().downcast::<RwLock<ObserverBroadcast<V>>>(),
|
||||||
|
self.update_hooks.clone()
|
||||||
|
) {
|
||||||
|
(Ok(view), Ok(cast), update_hooks) => Ok(ViewPort{view, cast, update_hooks}),
|
||||||
|
_ => Err(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View + ?Sized + 'static> From<ViewPort<V>> for AnyViewPort {
|
||||||
|
fn from(port: ViewPort<V>) -> Self {
|
||||||
|
AnyViewPort {
|
||||||
|
view: port.view as Arc<dyn Any + Send + Sync + 'static>,
|
||||||
|
cast: port.cast as Arc<dyn Any + Send + Sync + 'static>,
|
||||||
|
update_hooks: port.update_hooks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AnyOuterViewPort(AnyViewPort);
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AnyInnerViewPort(AnyViewPort);
|
||||||
|
|
||||||
|
impl AnyOuterViewPort {
|
||||||
|
pub fn downcast<V: View + ?Sized + 'static>(self) -> Result<OuterViewPort<V>, AnyViewPort> where V::Msg: Clone {
|
||||||
|
Ok(OuterViewPort(self.0.downcast::<V>()?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View + ?Sized + 'static> From<OuterViewPort<V>> for AnyOuterViewPort
|
||||||
|
where V::Msg: Clone {
|
||||||
|
fn from(port: OuterViewPort<V>) -> Self {
|
||||||
|
AnyOuterViewPort(AnyViewPort{
|
||||||
|
view: port.0.view as Arc<dyn Any + Send + Sync + 'static>,
|
||||||
|
cast: port.0.cast as Arc<dyn Any + Send + Sync + 'static>,
|
||||||
|
update_hooks: port.0.update_hooks
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AnyInnerViewPort {
|
||||||
|
pub fn downcast<V: View + ?Sized + 'static>(self) -> Result<InnerViewPort<V>, AnyViewPort> where V::Msg: Clone {
|
||||||
|
Ok(InnerViewPort(self.0.downcast::<V>()?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View + ?Sized + 'static> From<InnerViewPort<V>> for AnyInnerViewPort
|
||||||
|
where V::Msg: Clone {
|
||||||
|
fn from(port: InnerViewPort<V>) -> Self {
|
||||||
|
AnyInnerViewPort(AnyViewPort{
|
||||||
|
view: port.0.view as Arc<dyn Any + Send + Sync + 'static>,
|
||||||
|
cast: port.0.cast as Arc<dyn Any + Send + Sync + 'static>,
|
||||||
|
update_hooks: port.0.update_hooks
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
163
nested/src/core/type_term.rs
Normal file
163
nested/src/core/type_term.rs
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
use {
|
||||||
|
std::{
|
||||||
|
collections::HashMap
|
||||||
|
},
|
||||||
|
crate::{
|
||||||
|
bimap::Bimap,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub type TypeID = u64;
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub enum TypeTerm {
|
||||||
|
Type {
|
||||||
|
id: TypeID,
|
||||||
|
args: Vec<TypeTerm>
|
||||||
|
},
|
||||||
|
Num(i64)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypeTerm {
|
||||||
|
pub fn new(id: TypeID) -> Self {
|
||||||
|
TypeTerm::Type{ id, args: vec![] }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn arg(&mut self, t: TypeTerm) -> &mut Self {
|
||||||
|
if let TypeTerm::Type{ id: _, args } = self {
|
||||||
|
args.push(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn num_arg(&mut self, v: i64) -> &mut Self {
|
||||||
|
self.arg(TypeTerm::Num(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_str(s: &str, names: &HashMap<String, u64>) -> Option<Self> {
|
||||||
|
let mut term_stack = Vec::<Option<TypeTerm>>::new();
|
||||||
|
|
||||||
|
for token in s.split_whitespace() {
|
||||||
|
match token {
|
||||||
|
"(" => {
|
||||||
|
term_stack.push(None);
|
||||||
|
},
|
||||||
|
")" => {
|
||||||
|
let t = term_stack.pop().unwrap();
|
||||||
|
if term_stack.len() > 0 {
|
||||||
|
let f = term_stack.last_mut().unwrap();
|
||||||
|
if let Some(f) = f {
|
||||||
|
f.arg(t.unwrap());
|
||||||
|
} else {
|
||||||
|
//error
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
atom => {
|
||||||
|
let f = term_stack.last_mut().unwrap();
|
||||||
|
|
||||||
|
match f {
|
||||||
|
Some(f) =>
|
||||||
|
if atom.chars().nth(0).unwrap().is_numeric() {
|
||||||
|
f.num_arg(i64::from_str_radix(atom, 10).unwrap());
|
||||||
|
} else {
|
||||||
|
f.arg(TypeTerm::new(*names.get(atom).expect(&format!("invalid atom {}", atom))));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
*f = Some(TypeTerm::new(*names.get(atom).expect(&format!("invalid atom {}", atom))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
// only adds parenthesis where args.len > 0
|
||||||
|
pub fn to_str1(&self, names: &HashMap<u64, String>) -> String {
|
||||||
|
match self {
|
||||||
|
TypeTerm::Type{ id, args } =>
|
||||||
|
if args.len() > 0 {
|
||||||
|
format!(
|
||||||
|
"( {} {})",
|
||||||
|
names[id],
|
||||||
|
if args.len() > 0 {
|
||||||
|
args.iter().fold(
|
||||||
|
String::new(),
|
||||||
|
|str, term| format!("{}{} ", str, term.to_str1(names) )
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
names[id].clone()
|
||||||
|
},
|
||||||
|
|
||||||
|
TypeTerm::Num(n) =>
|
||||||
|
format!("{}", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// always adds an enclosing pair of parenthesis
|
||||||
|
pub fn to_str(&self, names: &HashMap<u64, String>) -> String {
|
||||||
|
match self {
|
||||||
|
TypeTerm::Type{ id, args } =>
|
||||||
|
format!(
|
||||||
|
"( {} {})",
|
||||||
|
names[id],
|
||||||
|
if args.len() > 0 {
|
||||||
|
args.iter().fold(
|
||||||
|
String::new(),
|
||||||
|
|str, term| format!("{}{} ", str, term.to_str1(names) )
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
}),
|
||||||
|
|
||||||
|
TypeTerm::Num(n) =>
|
||||||
|
format!("{}", n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub struct TypeDict {
|
||||||
|
typenames: Bimap::<String, u64>,
|
||||||
|
type_id_counter: u64
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TypeDict {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
TypeDict {
|
||||||
|
typenames: Bimap::new(),
|
||||||
|
type_id_counter: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_typename(&mut self, tn: String) {
|
||||||
|
self.typenames.insert(tn, self.type_id_counter);
|
||||||
|
self.type_id_counter += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn type_term_from_str(&self, typename: &str) -> Option<TypeTerm> {
|
||||||
|
TypeTerm::from_str(typename, &self.typenames.mλ)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn type_term_to_str(&self, term: &TypeTerm) -> String {
|
||||||
|
term.to_str(&self.typenames.my)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
28
nested/src/core/view.rs
Normal file
28
nested/src/core/view.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
|
||||||
|
/*\
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
View
|
||||||
|
<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
\*/
|
||||||
|
pub trait View : Send + Sync {
|
||||||
|
/// Notification message for the observers
|
||||||
|
type Msg : Send + Sync;
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::RwLock;
|
||||||
|
|
||||||
|
impl<V: View + ?Sized> View for RwLock<V> {
|
||||||
|
type Msg = V::Msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View + ?Sized> View for Arc<V> {
|
||||||
|
type Msg = V::Msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: View> View for Option<V> {
|
||||||
|
type Msg = V::Msg;
|
||||||
|
}
|
||||||
|
|
|
@ -1,113 +0,0 @@
|
||||||
use {
|
|
||||||
r3vi::{
|
|
||||||
view::{OuterViewPort, sequence::*},
|
|
||||||
buffer::{vec::*, index_hashmap::*}
|
|
||||||
},
|
|
||||||
crate::{
|
|
||||||
terminal::{
|
|
||||||
TerminalView, TerminalStyle, make_label
|
|
||||||
}
|
|
||||||
},
|
|
||||||
cgmath::Point2
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Message {
|
|
||||||
pub addr: Vec<usize>,
|
|
||||||
pub port: OuterViewPort<dyn TerminalView>
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Diagnostics {
|
|
||||||
fn get_msg_port(&self) -> OuterViewPort<dyn SequenceView<Item = Message>> {
|
|
||||||
VecBuffer::new().get_port().to_sequence()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn make_error(msg: OuterViewPort<dyn TerminalView>) -> Message {
|
|
||||||
let mut mb = IndexBuffer::new();
|
|
||||||
mb.insert_iter(vec![
|
|
||||||
(Point2::new(0, 0),
|
|
||||||
make_label("error: ")
|
|
||||||
.map_item(|_p,a| a
|
|
||||||
.add_style_back(TerminalStyle::bold(true))
|
|
||||||
.add_style_back(TerminalStyle::fg_color((200,0,0))))
|
|
||||||
),
|
|
||||||
(Point2::new(1, 0),
|
|
||||||
msg
|
|
||||||
.map_item(|_p,a| a
|
|
||||||
.add_style_back(TerminalStyle::fg_color((180,180,180))))
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
|
|
||||||
Message {
|
|
||||||
addr: vec![],
|
|
||||||
port: mb.get_port().flatten()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn make_warn(msg: OuterViewPort<dyn TerminalView>) -> Message {
|
|
||||||
let mut mb = IndexBuffer::new();
|
|
||||||
mb.insert_iter(vec![
|
|
||||||
(Point2::new(0, 0),
|
|
||||||
make_label("warning: ")
|
|
||||||
.map_item(|_p,a| a
|
|
||||||
.add_style_back(TerminalStyle::bold(true))
|
|
||||||
.add_style_back(TerminalStyle::fg_color((200,200,0))))
|
|
||||||
),
|
|
||||||
(Point2::new(1, 0),
|
|
||||||
msg
|
|
||||||
.map_item(|_p,a| a
|
|
||||||
.add_style_back(TerminalStyle::fg_color((180,180,180))))
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
|
|
||||||
Message {
|
|
||||||
addr: vec![],
|
|
||||||
port: mb.get_port().flatten()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn make_todo(msg: OuterViewPort<dyn TerminalView>) -> Message {
|
|
||||||
let mut mb = IndexBuffer::new();
|
|
||||||
mb.insert_iter(vec![
|
|
||||||
(Point2::new(0, 0),
|
|
||||||
make_label("todo: ")
|
|
||||||
.map_item(|_p,a| a
|
|
||||||
.add_style_back(TerminalStyle::bold(true))
|
|
||||||
.add_style_back(TerminalStyle::fg_color((180,180,250))))
|
|
||||||
),
|
|
||||||
(Point2::new(1, 0),
|
|
||||||
msg
|
|
||||||
.map_item(|_p,a| a
|
|
||||||
.add_style_back(TerminalStyle::fg_color((180,180,180))))
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
|
|
||||||
Message {
|
|
||||||
addr: vec![],
|
|
||||||
port: mb.get_port().flatten()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn make_info(msg: OuterViewPort<dyn TerminalView>) -> Message {
|
|
||||||
let mut mb = IndexBuffer::new();
|
|
||||||
mb.insert_iter(vec![
|
|
||||||
(Point2::new(0, 0),
|
|
||||||
make_label("info: ")
|
|
||||||
.map_item(|_p,a| a
|
|
||||||
.add_style_back(TerminalStyle::bold(true))
|
|
||||||
.add_style_back(TerminalStyle::fg_color((180,180,250))))
|
|
||||||
),
|
|
||||||
(Point2::new(1, 0),
|
|
||||||
msg
|
|
||||||
.map_item(|_p,a| a
|
|
||||||
.add_style_back(TerminalStyle::fg_color((180,180,180))))
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
|
|
||||||
Message {
|
|
||||||
addr: vec![],
|
|
||||||
port: mb.get_port().flatten()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,102 +0,0 @@
|
||||||
use {
|
|
||||||
r3vi::{
|
|
||||||
view::{
|
|
||||||
OuterViewPort,
|
|
||||||
singleton::*,
|
|
||||||
},
|
|
||||||
buffer::singleton::*
|
|
||||||
},
|
|
||||||
laddertypes::{TypeTerm},
|
|
||||||
crate::{
|
|
||||||
type_system::{Context, ReprTree},
|
|
||||||
terminal::{TerminalAtom},
|
|
||||||
tree::{NestedNode, TreeNavResult},
|
|
||||||
commander::{ObjCommander}
|
|
||||||
},
|
|
||||||
std::sync::Arc,
|
|
||||||
std::sync::RwLock
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn init_ctx( ctx: &mut Context ) {
|
|
||||||
ctx.add_node_ctor(
|
|
||||||
"Char",
|
|
||||||
Arc::new(|ctx: Arc<RwLock<Context>>, _ty: TypeTerm, depth: OuterViewPort<dyn SingletonView<Item = usize>>| {
|
|
||||||
Some(CharEditor::new_node(ctx, depth))
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CharEditor {
|
|
||||||
ctx: Arc<RwLock<Context>>,
|
|
||||||
data: SingletonBuffer<char>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ObjCommander for CharEditor {
|
|
||||||
fn send_cmd_obj(&mut self, cmd_obj: Arc<RwLock<ReprTree>>) -> TreeNavResult {
|
|
||||||
let cmd_obj = cmd_obj.read().unwrap();
|
|
||||||
let cmd_type = cmd_obj.get_type().clone();
|
|
||||||
|
|
||||||
if cmd_type == Context::parse(&self.ctx, "Char") {
|
|
||||||
if let Some(cmd_view) = cmd_obj.get_view::<dyn SingletonView<Item = char>>() {
|
|
||||||
let value = cmd_view.get();
|
|
||||||
|
|
||||||
if self.ctx.read().unwrap().meta_chars.contains(&value) {
|
|
||||||
TreeNavResult::Exit
|
|
||||||
} else {
|
|
||||||
self.data.set(value);
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CharEditor {
|
|
||||||
pub fn new(ctx: Arc<RwLock<Context>>) -> Self {
|
|
||||||
CharEditor {
|
|
||||||
ctx,
|
|
||||||
data: SingletonBuffer::new('\0')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_port(&self) -> OuterViewPort<dyn SingletonView<Item = char>> {
|
|
||||||
self.data.get_port()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&self) -> char {
|
|
||||||
self.get_port().get_view().unwrap().get()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_node(ctx0: Arc<RwLock<Context>>, depth: OuterViewPort<dyn SingletonView<Item = usize>>) -> NestedNode {
|
|
||||||
let data = SingletonBuffer::new('\0');
|
|
||||||
let ctx = ctx0.clone();
|
|
||||||
let editor = Arc::new(RwLock::new(CharEditor{ ctx, data: data.clone() }));
|
|
||||||
|
|
||||||
NestedNode::new(
|
|
||||||
ctx0.clone(),
|
|
||||||
ReprTree::new_leaf(
|
|
||||||
ctx0.read().unwrap().type_term_from_str("Char").unwrap(),
|
|
||||||
data.get_port().into()
|
|
||||||
),
|
|
||||||
depth
|
|
||||||
)
|
|
||||||
.set_view(data
|
|
||||||
.get_port()
|
|
||||||
.map(move |c| TerminalAtom::from(if c == '\0' { ' ' } else { c }))
|
|
||||||
.to_grid()
|
|
||||||
)
|
|
||||||
.set_cmd( editor.clone() )
|
|
||||||
.set_editor( editor.clone() )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
use crate::StringGen;
|
|
||||||
impl StringGen for CharEditor {
|
|
||||||
fn get_string(&self) -> String {
|
|
||||||
String::from(self.get())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
|
@ -1,70 +0,0 @@
|
||||||
use {
|
|
||||||
r3vi::{
|
|
||||||
view::{
|
|
||||||
InnerViewPort, OuterViewPort,
|
|
||||||
sequence::*,
|
|
||||||
},
|
|
||||||
buffer::{
|
|
||||||
vec::*
|
|
||||||
},
|
|
||||||
projection::projection_helper::*,
|
|
||||||
},
|
|
||||||
std::sync::{Arc, RwLock},
|
|
||||||
};
|
|
||||||
|
|
||||||
fn posint_add(
|
|
||||||
radix: usize,
|
|
||||||
a: impl SequenceView<Item = usize>,
|
|
||||||
b: impl SequenceView<Item = usize>,
|
|
||||||
) -> Vec<usize> {
|
|
||||||
let mut carry = 0;
|
|
||||||
let mut result = Vec::new();
|
|
||||||
|
|
||||||
for digit_idx in 0..std::cmp::max(a.len().unwrap_or(0), b.len().unwrap_or(0)) {
|
|
||||||
let sum = a.get(&digit_idx).unwrap_or(0) + b.get(&digit_idx).unwrap_or(0) + carry;
|
|
||||||
|
|
||||||
result.push(sum % radix);
|
|
||||||
|
|
||||||
carry = if sum > radix { sum - radix } else { 0 };
|
|
||||||
}
|
|
||||||
|
|
||||||
if carry > 0 {
|
|
||||||
result.push(carry);
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Add {
|
|
||||||
radix: usize,
|
|
||||||
a: Arc<dyn SequenceView<Item = usize>>, // PosInt, Little Endian
|
|
||||||
b: Arc<dyn SequenceView<Item = usize>>, // PosInt, Little Endian
|
|
||||||
c: VecBuffer<usize>,
|
|
||||||
_proj_helper: ProjectionHelper<usize, Self>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Add {
|
|
||||||
pub fn new(
|
|
||||||
radix: usize,
|
|
||||||
a: OuterViewPort<dyn SequenceView<Item = usize>>,
|
|
||||||
b: OuterViewPort<dyn SequenceView<Item = usize>>,
|
|
||||||
c: InnerViewPort<RwLock<Vec<usize>>>, //<dyn SequenceView<Item = usize>>
|
|
||||||
) -> Arc<RwLock<Self>> {
|
|
||||||
let mut proj_helper = ProjectionHelper::new(c.0.update_hooks.clone());
|
|
||||||
let add = Arc::new(RwLock::new(Add {
|
|
||||||
radix,
|
|
||||||
a: proj_helper.new_sequence_arg(0, a, |s: &mut Self, _digit_idx| s.update()),
|
|
||||||
b: proj_helper.new_sequence_arg(1, b, |s: &mut Self, _digit_idx| s.update()),
|
|
||||||
c: VecBuffer::with_port(c),
|
|
||||||
_proj_helper: proj_helper,
|
|
||||||
}));
|
|
||||||
add
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(&mut self) {
|
|
||||||
self.c.clear();
|
|
||||||
for digit in posint_add(self.radix, self.a.clone(), self.b.clone()) {
|
|
||||||
self.c.push(digit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,139 +0,0 @@
|
||||||
|
|
||||||
use {
|
|
||||||
r3vi::{
|
|
||||||
view::{OuterViewPort, singleton::*}
|
|
||||||
},
|
|
||||||
laddertypes::{TypeTerm},
|
|
||||||
crate::{
|
|
||||||
type_system::{Context},
|
|
||||||
editors::{
|
|
||||||
list::*,
|
|
||||||
integer::*
|
|
||||||
},
|
|
||||||
type_system::{MorphismTypePattern},
|
|
||||||
},
|
|
||||||
std::sync::{Arc, RwLock}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn init_ctx(ctx: &mut Context) {
|
|
||||||
ctx.add_typename("MachineInt".into());
|
|
||||||
ctx.add_typename("u32".into());
|
|
||||||
ctx.add_typename("u64".into());
|
|
||||||
ctx.add_typename("LittleEndian".into());
|
|
||||||
ctx.add_typename("BigEndian".into());
|
|
||||||
|
|
||||||
ctx.add_node_ctor(
|
|
||||||
"Digit", Arc::new(
|
|
||||||
|ctx: Arc<RwLock<Context>>, ty: TypeTerm, depth: OuterViewPort<dyn SingletonView<Item = usize>>| {
|
|
||||||
match ty {
|
|
||||||
TypeTerm::App(args) => {
|
|
||||||
if args.len() > 1 {
|
|
||||||
match args[1] {
|
|
||||||
TypeTerm::Num(radix) => {
|
|
||||||
let node = DigitEditor::new(ctx.clone(), radix as u32).into_node(depth);
|
|
||||||
Some(
|
|
||||||
node
|
|
||||||
)
|
|
||||||
},
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
ctx.add_list_typename("PosInt".into());
|
|
||||||
let pattern = MorphismTypePattern {
|
|
||||||
src_tyid: ctx.get_typeid("List"),
|
|
||||||
dst_tyid: ctx.get_typeid("PosInt").unwrap()
|
|
||||||
};
|
|
||||||
ctx.add_morphism(pattern,
|
|
||||||
Arc::new(
|
|
||||||
|mut node, dst_type| {
|
|
||||||
// todo: check src_type parameter to be ( Digit radix )
|
|
||||||
|
|
||||||
match dst_type {
|
|
||||||
TypeTerm::App(args) => {
|
|
||||||
if args.len() > 1 {
|
|
||||||
match args[1] {
|
|
||||||
TypeTerm::Num(_radix) => {
|
|
||||||
PTYListController::for_node(
|
|
||||||
&mut node,
|
|
||||||
Some(','),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
|
|
||||||
PTYListStyle::for_node(
|
|
||||||
&mut node,
|
|
||||||
("0d", "", "")
|
|
||||||
);
|
|
||||||
|
|
||||||
Some(node)
|
|
||||||
},
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
ctx.add_node_ctor(
|
|
||||||
"PosInt", Arc::new(
|
|
||||||
|ctx0: Arc<RwLock<Context>>, dst_typ: TypeTerm, depth: OuterViewPort<dyn SingletonView<Item = usize>>| {
|
|
||||||
match dst_typ.clone() {
|
|
||||||
TypeTerm::App(args) => {
|
|
||||||
if args.len() > 1 {
|
|
||||||
match args[1] {
|
|
||||||
TypeTerm::Num(radix) => {
|
|
||||||
let ctx = ctx0.read().unwrap();
|
|
||||||
let mut node = Context::make_node(
|
|
||||||
&ctx0,
|
|
||||||
TypeTerm::App(vec![
|
|
||||||
TypeTerm::TypeID(ctx.get_typeid("List").unwrap()),
|
|
||||||
TypeTerm::TypeID(
|
|
||||||
ctx.get_typeid("Digit").unwrap()
|
|
||||||
)
|
|
||||||
.num_arg(radix)
|
|
||||||
.clone()
|
|
||||||
.into()
|
|
||||||
]),
|
|
||||||
depth.map(|d| d+1)
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
node = node.morph(dst_typ);
|
|
||||||
|
|
||||||
Some(node)
|
|
||||||
}
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
ctx.add_typename("Date".into());
|
|
||||||
ctx.add_typename("ISO-8601".into());
|
|
||||||
ctx.add_typename("TimeSince".into());
|
|
||||||
ctx.add_typename("UnixEpoch".into());
|
|
||||||
ctx.add_typename("AnnoDomini".into());
|
|
||||||
ctx.add_typename("Epoch".into());
|
|
||||||
ctx.add_typename("Duration".into());
|
|
||||||
ctx.add_typename("Seconds".into());
|
|
||||||
ctx.add_typename("ℕ".into());
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,239 +0,0 @@
|
||||||
use {
|
|
||||||
r3vi::{
|
|
||||||
view::{
|
|
||||||
OuterViewPort,
|
|
||||||
singleton::*,
|
|
||||||
},
|
|
||||||
buffer::{
|
|
||||||
singleton::*,
|
|
||||||
vec::*,
|
|
||||||
index_hashmap::*
|
|
||||||
}
|
|
||||||
},
|
|
||||||
laddertypes::{TypeTerm},
|
|
||||||
crate::{
|
|
||||||
type_system::{Context, ReprTree},
|
|
||||||
editors::list::{ListCmd, PTYListController, PTYListStyle},
|
|
||||||
terminal::{
|
|
||||||
TerminalAtom, TerminalStyle, make_label
|
|
||||||
},
|
|
||||||
diagnostics::{Message},
|
|
||||||
tree::{NestedNode, TreeNav, TreeNavResult, TreeCursor},
|
|
||||||
commander::ObjCommander
|
|
||||||
},
|
|
||||||
std::sync::Arc,
|
|
||||||
std::sync::RwLock,
|
|
||||||
std::iter::FromIterator,
|
|
||||||
cgmath::{Point2}
|
|
||||||
};
|
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
|
|
||||||
pub struct DigitEditor {
|
|
||||||
ctx: Arc<RwLock<Context>>,
|
|
||||||
radix: u32,
|
|
||||||
data: SingletonBuffer<Option<char>>,
|
|
||||||
msg: VecBuffer<Message>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ObjCommander for DigitEditor {
|
|
||||||
fn send_cmd_obj(&mut self, cmd_obj: Arc<RwLock<ReprTree>>) -> TreeNavResult {
|
|
||||||
let cmd_obj = cmd_obj.read().unwrap();
|
|
||||||
let cmd_type = cmd_obj.get_type().clone();
|
|
||||||
|
|
||||||
if cmd_type == Context::parse(&self.ctx, "Char") {
|
|
||||||
if let Some(cmd_view) = cmd_obj.get_view::<dyn SingletonView<Item = char>>() {
|
|
||||||
let c = cmd_view.get();
|
|
||||||
|
|
||||||
self.msg.clear();
|
|
||||||
|
|
||||||
if self.ctx.read().unwrap().meta_chars.contains(&c) {
|
|
||||||
eprintln!("digitedit: meta char");
|
|
||||||
return TreeNavResult::Exit;
|
|
||||||
|
|
||||||
} else if c.to_digit(self.radix).is_none() {
|
|
||||||
/* in case the character c is not in the range of digit-chars,
|
|
||||||
add a message to the diagnostics view
|
|
||||||
*/
|
|
||||||
|
|
||||||
let message = IndexBuffer::from_iter(vec![
|
|
||||||
(Point2::new(1, 0), make_label("invalid digit '")),
|
|
||||||
(Point2::new(2, 0), make_label(&format!("{}", c))
|
|
||||||
.map_item(|_p,a| a.add_style_back(TerminalStyle::fg_color((140,140,250))))),
|
|
||||||
(Point2::new(3, 0), make_label("'"))
|
|
||||||
]);
|
|
||||||
|
|
||||||
self.msg.push(crate::diagnostics::make_error(message.get_port().flatten()));
|
|
||||||
self.data.set(Some(c));
|
|
||||||
} else {
|
|
||||||
self.data.set(Some(c));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DigitEditor {
|
|
||||||
pub fn new(ctx: Arc<RwLock<Context>>, radix: u32) -> Self {
|
|
||||||
DigitEditor {
|
|
||||||
ctx,
|
|
||||||
radix,
|
|
||||||
data: SingletonBuffer::new(None),
|
|
||||||
msg: VecBuffer::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_node(self, depth: OuterViewPort<dyn SingletonView<Item = usize>>) -> NestedNode {
|
|
||||||
let data = self.get_data();
|
|
||||||
let editor = Arc::new(RwLock::new(self));
|
|
||||||
let ed = editor.write().unwrap();
|
|
||||||
let r = ed.radix;
|
|
||||||
|
|
||||||
NestedNode::new(ed.ctx.clone(), data, depth)
|
|
||||||
.set_cmd(editor.clone())
|
|
||||||
.set_view(
|
|
||||||
ed.data
|
|
||||||
.get_port()
|
|
||||||
.map(move |c| {
|
|
||||||
TerminalAtom::new(
|
|
||||||
c.unwrap_or('?'),
|
|
||||||
if c.unwrap_or('?').to_digit(r).is_some() {
|
|
||||||
TerminalStyle::fg_color((90, 160, 90))
|
|
||||||
} else {
|
|
||||||
//TerminalStyle::bg_color((90, 10, 10))
|
|
||||||
TerminalStyle::fg_color((200, 40, 40))
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.to_grid()
|
|
||||||
)
|
|
||||||
.set_diag(
|
|
||||||
ed.msg.get_port().to_sequence()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_data_port(&self) -> OuterViewPort<dyn SingletonView<Item = Option<u32>>> {
|
|
||||||
let radix = self.radix;
|
|
||||||
self.data.get_port().map(move |c| c?.to_digit(radix))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_type(&self) -> TypeTerm {
|
|
||||||
TypeTerm::TypeID(self.ctx.read().unwrap().get_typeid("Digit").unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_data(&self) -> Arc<RwLock<ReprTree>> {
|
|
||||||
ReprTree::ascend(
|
|
||||||
&ReprTree::new_leaf(
|
|
||||||
self.ctx.read().unwrap().type_term_from_str("<Seq u32>").unwrap(),
|
|
||||||
self.get_data_port().into()
|
|
||||||
),
|
|
||||||
self.get_type()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub struct PosIntEditor {
|
|
||||||
radix: u32,
|
|
||||||
digits: NestedNode,
|
|
||||||
|
|
||||||
// todo: endianness
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PosIntEditor {
|
|
||||||
pub fn new(ctx: Arc<RwLock<Context>>, radix: u32) -> Self {
|
|
||||||
let mut node = Context::make_node(
|
|
||||||
&ctx,
|
|
||||||
Context::parse(&ctx, format!("<List <Digit {}>>", radix).as_str()),
|
|
||||||
r3vi::buffer::singleton::SingletonBuffer::new(0).get_port()
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
// Set Type
|
|
||||||
node.data = ReprTree::ascend(
|
|
||||||
&node.data.clone(),
|
|
||||||
TypeTerm::App(vec![
|
|
||||||
TypeTerm::TypeID(ctx.read().unwrap().get_typeid("PosInt").unwrap()),
|
|
||||||
TypeTerm::Num(radix as i64).into(),
|
|
||||||
TypeTerm::TypeID(ctx.read().unwrap().get_typeid("BigEndian").unwrap())
|
|
||||||
]
|
|
||||||
));
|
|
||||||
|
|
||||||
PTYListController::for_node( &mut node, Some(' '), None );
|
|
||||||
PTYListStyle::for_node( &mut node,
|
|
||||||
(
|
|
||||||
match radix {
|
|
||||||
2 => "0b".into(),
|
|
||||||
8 => "0o".into(),
|
|
||||||
10 => "0d".into(),
|
|
||||||
16 => "0x".into(),
|
|
||||||
_ => "".into()
|
|
||||||
},
|
|
||||||
"".into(),
|
|
||||||
"".into()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
PosIntEditor {
|
|
||||||
radix,
|
|
||||||
digits: node
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_u64(ctx: Arc<RwLock<Context>>, radix: u32, value: u64) -> Self {
|
|
||||||
let mut edit = PosIntEditor::new(ctx, radix);
|
|
||||||
edit.set_value_u64( value );
|
|
||||||
edit
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_value_u64(&mut self, mut value: u64) {
|
|
||||||
self.digits.send_cmd_obj(ListCmd::Clear.into_repr_tree(&self.digits.ctx));
|
|
||||||
|
|
||||||
while value > 0 {
|
|
||||||
let digit_val = (value % self.radix as u64) as u32;
|
|
||||||
value /= self.radix as u64;
|
|
||||||
|
|
||||||
// if BigEndian
|
|
||||||
self.digits.goto(TreeCursor::home());
|
|
||||||
|
|
||||||
self.digits.send_cmd_obj(ReprTree::from_char(&self.digits.ctx, char::from_digit(digit_val, self.radix).expect("invalid digit")));
|
|
||||||
}
|
|
||||||
self.digits.goto(TreeCursor::none());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_node(self) -> NestedNode {
|
|
||||||
self.digits
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
pub fn get_data_port(&self) -> OuterViewPort<dyn SequenceView<Item = u32>> {
|
|
||||||
let radix = self.radix;
|
|
||||||
self.digits
|
|
||||||
.get_data_port()
|
|
||||||
.filter_map(move |digit_editor| {
|
|
||||||
digit_editor.read().unwrap().data.get()?.to_digit(radix)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_value(&self) -> u32 {
|
|
||||||
let mut value = 0;
|
|
||||||
let mut weight = 1;
|
|
||||||
for digit_value in self
|
|
||||||
.get_data_port()
|
|
||||||
.get_view()
|
|
||||||
.unwrap()
|
|
||||||
.iter()
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.into_iter()
|
|
||||||
.rev()
|
|
||||||
{
|
|
||||||
value += digit_value * weight;
|
|
||||||
weight *= self.radix;
|
|
||||||
}
|
|
||||||
|
|
||||||
value
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,170 +0,0 @@
|
||||||
use {
|
|
||||||
r3vi::{
|
|
||||||
view::{singleton::*}
|
|
||||||
},
|
|
||||||
crate::{
|
|
||||||
editors::list::{ListEditor, ListCursor, ListCursorMode},
|
|
||||||
type_system::{Context, ReprTree},
|
|
||||||
tree::{NestedNode, TreeNav, TreeNavResult, TreeCursor},
|
|
||||||
commander::{ObjCommander}
|
|
||||||
},
|
|
||||||
std::sync::{Arc, RwLock}
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
|
||||||
pub enum ListCmd {
|
|
||||||
DeletePxev,
|
|
||||||
DeleteNexd,
|
|
||||||
JoinNexd,
|
|
||||||
JoinPxev,
|
|
||||||
Split,
|
|
||||||
Clear,
|
|
||||||
Close,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ListCmd {
|
|
||||||
pub fn into_repr_tree(self, ctx: &Arc<RwLock<Context>>) -> Arc<RwLock<ReprTree>> {
|
|
||||||
let buf = r3vi::buffer::singleton::SingletonBuffer::new(self);
|
|
||||||
ReprTree::new_leaf(
|
|
||||||
Context::parse(ctx, "ListCmd"),
|
|
||||||
buf.get_port().into()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ObjCommander for ListEditor {
|
|
||||||
fn send_cmd_obj(&mut self, cmd_obj: Arc<RwLock<ReprTree>>) -> TreeNavResult {
|
|
||||||
let cmd_repr = cmd_obj.read().unwrap();
|
|
||||||
|
|
||||||
if let Some(view) = cmd_repr.get_view::<dyn SingletonView<Item = NestedNode>>() {
|
|
||||||
let node = view.get();
|
|
||||||
let cur = self.cursor.get();
|
|
||||||
|
|
||||||
if let Some(idx) = cur.idx {
|
|
||||||
match cur.mode {
|
|
||||||
ListCursorMode::Select => {
|
|
||||||
*self.data.get_mut(idx as usize) = Arc::new(RwLock::new(node));
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
ListCursorMode::Insert => {
|
|
||||||
self.insert(Arc::new(RwLock::new(node)));
|
|
||||||
self.cursor.set(ListCursor{ idx: Some(idx+1), mode: ListCursorMode::Insert });
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if let Some(cmd) = cmd_repr.get_view::<dyn SingletonView<Item = ListCmd>>() {
|
|
||||||
eprintln!("pty-list-editor some list cmmd");
|
|
||||||
|
|
||||||
let cur = self.cursor.get();
|
|
||||||
drop(cmd_repr);
|
|
||||||
|
|
||||||
match cmd.get() {
|
|
||||||
ListCmd::Clear => {
|
|
||||||
self.clear();
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
if let Some(idx) = cur.idx {
|
|
||||||
match cur.mode {
|
|
||||||
ListCursorMode::Select => {
|
|
||||||
if let Some(mut item) = self.get_item().clone() {
|
|
||||||
let item_cur = item.get_cursor();
|
|
||||||
|
|
||||||
match cmd.get() {
|
|
||||||
ListCmd::DeletePxev => {
|
|
||||||
eprintln!("SELECT: delete pxev");
|
|
||||||
if idx > 0
|
|
||||||
&& item_cur.tree_addr.iter().fold(
|
|
||||||
true,
|
|
||||||
|is_zero, x| is_zero && (*x == 0)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
self.listlist_join_pxev(idx);
|
|
||||||
TreeNavResult::Continue
|
|
||||||
} else {
|
|
||||||
item.send_cmd_obj(cmd_obj)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ListCmd::DeleteNexd => {
|
|
||||||
let item_cur = item.get_cursor_warp();
|
|
||||||
let next_idx = idx as usize + 1;
|
|
||||||
|
|
||||||
if next_idx < self.data.len()
|
|
||||||
&& item_cur.tree_addr.iter().fold(
|
|
||||||
true,
|
|
||||||
|is_end, x| is_end && (*x == -1)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
self.listlist_join_nexd(idx as usize);
|
|
||||||
TreeNavResult::Continue
|
|
||||||
} else {
|
|
||||||
item.send_cmd_obj(cmd_obj)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ListCmd::Split => {
|
|
||||||
self.listlist_split();
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => {
|
|
||||||
item.send_cmd_obj(cmd_obj);
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
eprintln!("ptylist: no item");
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
ListCursorMode::Insert => {
|
|
||||||
match cmd.get() {
|
|
||||||
ListCmd::DeletePxev => {
|
|
||||||
eprintln!("INSERT: delete pxev");
|
|
||||||
self.delete_pxev();
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
ListCmd::DeleteNexd => {
|
|
||||||
self.delete_nexd();
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
ListCmd::Split => {
|
|
||||||
self.split();
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
ListCmd::Close => {
|
|
||||||
self.goto(TreeCursor::none());
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
_ =>{
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
eprintln!("ptylist: cursor has no idx");
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
if let Some(cur_item) = self.get_item_mut() {
|
|
||||||
drop(cmd_repr);
|
|
||||||
cur_item.write().unwrap().send_cmd_obj(cmd_obj);
|
|
||||||
TreeNavResult::Continue
|
|
||||||
} else {
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
use {
|
|
||||||
r3vi::{view::{OuterViewPort, singleton::*}},
|
|
||||||
laddertypes::{TypeTerm},
|
|
||||||
crate::{
|
|
||||||
type_system::{Context},
|
|
||||||
editors::list::{ListEditor, PTYListController, PTYListStyle}
|
|
||||||
},
|
|
||||||
std::sync::{Arc, RwLock}
|
|
||||||
};
|
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
|
|
||||||
pub fn init_ctx(ctx: &mut Context) {
|
|
||||||
ctx.add_typename("ListCmd".into());
|
|
||||||
ctx.add_list_typename("List".into());
|
|
||||||
|
|
||||||
ctx.add_node_ctor(
|
|
||||||
"List", Arc::new(
|
|
||||||
|ctx: Arc<RwLock<Context>>, ty: TypeTerm, depth: OuterViewPort<dyn SingletonView<Item = usize>>| {
|
|
||||||
match ty {
|
|
||||||
TypeTerm::App(args) => {
|
|
||||||
if args.len() > 1 {
|
|
||||||
let typ = args[1].clone();
|
|
||||||
|
|
||||||
let mut node = ListEditor::new(ctx.clone(), typ).into_node(depth);
|
|
||||||
|
|
||||||
PTYListController::for_node( &mut node, Some(','), Some('}') );
|
|
||||||
PTYListStyle::for_node( &mut node, ("{",", ","}") );
|
|
||||||
|
|
||||||
Some(node)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
|
|
||||||
pub enum ListCursorMode {
|
|
||||||
Insert,
|
|
||||||
Select
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ListCursorMode {
|
|
||||||
fn default() -> Self {
|
|
||||||
ListCursorMode::Select
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
|
|
||||||
pub struct ListCursor {
|
|
||||||
pub mode: ListCursorMode,
|
|
||||||
pub idx: Option<isize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ListCursor {
|
|
||||||
pub fn home() -> Self {
|
|
||||||
ListCursor {
|
|
||||||
mode: ListCursorMode::Insert,
|
|
||||||
idx: Some(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn none() -> Self {
|
|
||||||
ListCursor {
|
|
||||||
mode: ListCursorMode::Insert,
|
|
||||||
idx: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for ListCursor {
|
|
||||||
fn default() -> Self {
|
|
||||||
ListCursor::home()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,494 +0,0 @@
|
||||||
use {
|
|
||||||
r3vi::{
|
|
||||||
view::{ViewPort, OuterViewPort, singleton::*, sequence::*},
|
|
||||||
buffer::{singleton::*, vec::*}
|
|
||||||
},
|
|
||||||
laddertypes::{TypeTerm},
|
|
||||||
crate::{
|
|
||||||
type_system::{Context, ReprTree},
|
|
||||||
editors::list::{ListCursor, ListCursorMode, ListCmd},
|
|
||||||
tree::{NestedNode, TreeNav, TreeCursor},
|
|
||||||
diagnostics::Diagnostics,
|
|
||||||
commander::ObjCommander
|
|
||||||
},
|
|
||||||
std::sync::{Arc, RwLock}
|
|
||||||
};
|
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
|
|
||||||
pub struct ListEditor {
|
|
||||||
pub(super) cursor: SingletonBuffer<ListCursor>,
|
|
||||||
|
|
||||||
// todo: (?) remove RwLock<..> around NestedNode ??
|
|
||||||
pub data: VecBuffer< Arc<RwLock<NestedNode>> >,
|
|
||||||
|
|
||||||
pub spillbuf: Arc<RwLock<Vec<Arc<RwLock<NestedNode>>>>>,
|
|
||||||
|
|
||||||
pub(super) addr_port: OuterViewPort<dyn SequenceView<Item = isize>>,
|
|
||||||
pub(super) mode_port: OuterViewPort<dyn SingletonView<Item = ListCursorMode>>,
|
|
||||||
|
|
||||||
depth: OuterViewPort<dyn SingletonView<Item = usize>>,
|
|
||||||
|
|
||||||
pub(crate) ctx: Arc<RwLock<Context>>,
|
|
||||||
|
|
||||||
/// item type
|
|
||||||
pub(super) typ: TypeTerm,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ListEditor {
|
|
||||||
pub fn new(
|
|
||||||
ctx: Arc<RwLock<Context>>,
|
|
||||||
typ: TypeTerm,
|
|
||||||
) -> Self {
|
|
||||||
let cursor = SingletonBuffer::new(ListCursor::default());
|
|
||||||
let data : VecBuffer<Arc<RwLock<NestedNode>>> = VecBuffer::new();
|
|
||||||
|
|
||||||
ListEditor {
|
|
||||||
mode_port: cursor
|
|
||||||
.get_port()
|
|
||||||
.map({
|
|
||||||
let data = data.clone();
|
|
||||||
move |c| {
|
|
||||||
let ip = SingletonBuffer::new(c.mode).get_port();
|
|
||||||
match c.mode {
|
|
||||||
ListCursorMode::Insert => ip,
|
|
||||||
ListCursorMode::Select => {
|
|
||||||
if let Some(idx) = c.idx {
|
|
||||||
if idx >= 0 && idx < data.len() as isize {
|
|
||||||
data.get(idx as usize).read().unwrap().get_mode_view()
|
|
||||||
} else {
|
|
||||||
eprintln!("ListEditor::mode_port invalid cursor idx");
|
|
||||||
ip
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ip
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.flatten(),
|
|
||||||
|
|
||||||
addr_port: VecBuffer::<OuterViewPort<dyn SequenceView<Item = isize>>>::with_data(
|
|
||||||
vec![
|
|
||||||
cursor.get_port()
|
|
||||||
.to_sequence()
|
|
||||||
.filter_map(|cur| cur.idx),
|
|
||||||
cursor.get_port()
|
|
||||||
.map({
|
|
||||||
let data = data.clone();
|
|
||||||
move |cur| {
|
|
||||||
if cur.mode == ListCursorMode::Select {
|
|
||||||
if let Some(idx) = cur.idx {
|
|
||||||
if idx >= 0 && idx < data.len() as isize {
|
|
||||||
return data.get(idx as usize).read().unwrap().get_addr_view();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
OuterViewPort::default()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.to_sequence()
|
|
||||||
.flatten()
|
|
||||||
])
|
|
||||||
.get_port()
|
|
||||||
.to_sequence()
|
|
||||||
.flatten(),
|
|
||||||
cursor,
|
|
||||||
data,
|
|
||||||
spillbuf: Arc::new(RwLock::new(Vec::new())),
|
|
||||||
ctx,
|
|
||||||
typ,
|
|
||||||
depth: SingletonBuffer::new(0).get_port()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_node(mut self, depth: OuterViewPort<dyn SingletonView<Item = usize>>) -> NestedNode {
|
|
||||||
let data = self.get_data();
|
|
||||||
let ctx = self.ctx.clone();
|
|
||||||
|
|
||||||
self.depth = depth.clone();
|
|
||||||
let editor = Arc::new(RwLock::new(self));
|
|
||||||
|
|
||||||
let e = editor.read().unwrap();
|
|
||||||
|
|
||||||
let mut node = NestedNode::new(ctx, data, depth)
|
|
||||||
.set_editor(editor.clone())
|
|
||||||
.set_nav(editor.clone())
|
|
||||||
.set_cmd(editor.clone())
|
|
||||||
.set_diag(e
|
|
||||||
.get_data_port()
|
|
||||||
.enumerate()
|
|
||||||
.map(
|
|
||||||
|(idx, item_editor)| {
|
|
||||||
let idx = *idx;
|
|
||||||
item_editor
|
|
||||||
.get_msg_port()
|
|
||||||
.map(
|
|
||||||
move |msg| {
|
|
||||||
let mut msg = msg.clone();
|
|
||||||
msg.addr.insert(0, idx);
|
|
||||||
msg
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.flatten()
|
|
||||||
);
|
|
||||||
|
|
||||||
node.spillbuf = e.spillbuf.clone();
|
|
||||||
node
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_item_type(&self) -> TypeTerm {
|
|
||||||
self.typ.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_seq_type(&self) -> TypeTerm {
|
|
||||||
TypeTerm::App(vec![
|
|
||||||
TypeTerm::TypeID(self.ctx.read().unwrap().get_typeid("List").unwrap()),
|
|
||||||
self.get_item_type().into()
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_cursor_port(&self) -> OuterViewPort<dyn SingletonView<Item = ListCursor>> {
|
|
||||||
self.cursor.get_port()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_data_port(&self) -> OuterViewPort<dyn SequenceView<Item = NestedNode>> {
|
|
||||||
self.data.get_port().to_sequence().map(
|
|
||||||
|x| x.read().unwrap().clone()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_data(&self) -> Arc<RwLock<ReprTree>> {
|
|
||||||
let data_view = self.get_data_port();
|
|
||||||
ReprTree::new_leaf(
|
|
||||||
self.get_seq_type(),
|
|
||||||
data_view.into()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_item(&self) -> Option<NestedNode> {
|
|
||||||
if let Some(idx) = self.cursor.get().idx {
|
|
||||||
let idx = crate::utils::modulo(idx as isize, self.data.len() as isize) as usize;
|
|
||||||
if idx < self.data.len() {
|
|
||||||
Some(self.data.get(idx).read().unwrap().clone())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_item_mut(&mut self) -> Option<MutableVecAccess<Arc<RwLock<NestedNode>>>> {
|
|
||||||
if let Some(idx) = self.cursor.get().idx {
|
|
||||||
let idx = crate::utils::modulo(idx as isize, self.data.len() as isize) as usize;
|
|
||||||
if idx < self.data.len() {
|
|
||||||
Some(self.data.get_mut(idx))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// is the element-type also a list-like editor (i.e. impls TreeNav)
|
|
||||||
pub fn is_listlist(&self) -> bool {
|
|
||||||
self.ctx.read().unwrap().is_list_type(&self.typ)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// delete all items
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
let mut b = self.spillbuf.write().unwrap();
|
|
||||||
for i in 0..self.data.len() {
|
|
||||||
b.push( self.data.get(i) );
|
|
||||||
}
|
|
||||||
|
|
||||||
self.data.clear();
|
|
||||||
self.cursor.set(ListCursor::home());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// delete item before the cursor
|
|
||||||
pub fn delete_pxev(&mut self) {
|
|
||||||
let mut cur = self.cursor.get();
|
|
||||||
if let Some(idx) = cur.idx {
|
|
||||||
if idx > 0 && idx <= self.data.len() as isize {
|
|
||||||
cur.idx = Some(idx as isize - 1);
|
|
||||||
self.cursor.set(cur);
|
|
||||||
self.data.remove(idx as usize - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// delete item after the cursor
|
|
||||||
pub fn delete_nexd(&mut self) {
|
|
||||||
if let Some(idx) = self.cursor.get().idx {
|
|
||||||
if idx < self.data.len() as isize {
|
|
||||||
self.data.remove(idx as usize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// insert a new element
|
|
||||||
pub fn insert(&mut self, item: Arc<RwLock<NestedNode>>) {
|
|
||||||
eprintln!("list insert");
|
|
||||||
|
|
||||||
item.read().unwrap().depth.0.set_view(
|
|
||||||
self.depth.map(|d| d+1).get_view()
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut cur = self.cursor.get();
|
|
||||||
if let Some(idx) = cur.idx {
|
|
||||||
match cur.mode {
|
|
||||||
ListCursorMode::Insert => {
|
|
||||||
self.data.insert(idx as usize, item.clone());
|
|
||||||
if self.is_listlist() {
|
|
||||||
cur.mode = ListCursorMode::Select;
|
|
||||||
} else {
|
|
||||||
eprintln!("list insert: is not a listlist ({:?})", self.typ);
|
|
||||||
item.write().unwrap().goto(TreeCursor::none());
|
|
||||||
cur.idx = Some(idx + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ListCursorMode::Select => {
|
|
||||||
self.data.insert(1 + idx as usize, item.clone());
|
|
||||||
if self.is_listlist() {
|
|
||||||
cur.idx = Some(idx + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.cursor.set(cur);
|
|
||||||
} else {
|
|
||||||
//eprintln!("insert: no cursor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// split the list off at the current cursor position and return the second half
|
|
||||||
pub fn split(&mut self) {
|
|
||||||
eprintln!("split");
|
|
||||||
let cur = self.cursor.get();
|
|
||||||
if let Some(idx) = cur.idx {
|
|
||||||
let idx = idx as usize;
|
|
||||||
for _ in idx .. self.data.len() {
|
|
||||||
self.spillbuf.write().unwrap().push(
|
|
||||||
self.data.get(idx)
|
|
||||||
);
|
|
||||||
self.data.remove(idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* in case the split leaves an empty item-list
|
|
||||||
* as a last element, remove it
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
if self.is_listlist() {
|
|
||||||
if idx > 0 && idx < self.data.len()+1 {
|
|
||||||
/* we are in insert mode,
|
|
||||||
* get element before cursor
|
|
||||||
*/
|
|
||||||
let prev_idx = idx - 1;
|
|
||||||
let prev_node = self.data.get(prev_idx);
|
|
||||||
let prev_node = prev_node.read().unwrap();
|
|
||||||
|
|
||||||
if prev_node.get_data_view::<dyn SequenceView<Item = NestedNode>>(vec![].into_iter()).iter().count() == 0 {
|
|
||||||
drop(prev_node);
|
|
||||||
self.data.remove(prev_idx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn listlist_split(&mut self) {
|
|
||||||
eprintln!("listlist split");
|
|
||||||
let cur = self.get_cursor();
|
|
||||||
|
|
||||||
if let Some(mut item) = self.get_item().clone() {
|
|
||||||
item.send_cmd_obj(ListCmd::Split.into_repr_tree(&self.ctx));
|
|
||||||
|
|
||||||
if cur.tree_addr.len() < 3 {
|
|
||||||
item.goto(TreeCursor::none());
|
|
||||||
|
|
||||||
self.set_leaf_mode(ListCursorMode::Insert);
|
|
||||||
self.nexd();
|
|
||||||
|
|
||||||
let mut b = item.spillbuf.write().unwrap();
|
|
||||||
let mut tail_node = Context::make_node(&self.ctx, self.typ.clone(), self.depth.map(|d| d+1)).unwrap();
|
|
||||||
tail_node.goto(TreeCursor::home());
|
|
||||||
|
|
||||||
for node in b.iter() {
|
|
||||||
eprintln!("splid :send to tail node");
|
|
||||||
tail_node
|
|
||||||
.send_cmd_obj(
|
|
||||||
ReprTree::new_leaf(
|
|
||||||
Context::parse(&self.ctx, "NestedNode"),
|
|
||||||
SingletonBuffer::<NestedNode>::new(
|
|
||||||
node.read().unwrap().clone()
|
|
||||||
).get_port().into()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
b.clear();
|
|
||||||
drop(b);
|
|
||||||
drop(item);
|
|
||||||
|
|
||||||
tail_node.goto(TreeCursor::home());
|
|
||||||
if cur.tree_addr.len() > 1 {
|
|
||||||
tail_node.dn();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.insert(
|
|
||||||
Arc::new(RwLock::new(tail_node))
|
|
||||||
);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
self.up();
|
|
||||||
self.listlist_split();
|
|
||||||
self.dn();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn listlist_join_pxev(&mut self, idx: isize) {
|
|
||||||
{
|
|
||||||
let cur_editor = self.data.get(idx as usize);
|
|
||||||
let pxv_editor = self.data.get(idx as usize-1);
|
|
||||||
let mut cur_editor = cur_editor.write().unwrap();
|
|
||||||
let mut pxv_editor = pxv_editor.write().unwrap();
|
|
||||||
|
|
||||||
let oc0 = cur_editor.get_cursor();
|
|
||||||
|
|
||||||
// tell cur_editor move all its elements into its spill-buffer
|
|
||||||
cur_editor.goto(TreeCursor::none());
|
|
||||||
cur_editor.send_cmd_obj(
|
|
||||||
ListCmd::Clear.into_repr_tree( &self.ctx )
|
|
||||||
);
|
|
||||||
|
|
||||||
pxv_editor.goto(TreeCursor {
|
|
||||||
tree_addr: vec![-1],
|
|
||||||
leaf_mode: ListCursorMode::Insert
|
|
||||||
});
|
|
||||||
|
|
||||||
let old_cur = pxv_editor.get_cursor();
|
|
||||||
|
|
||||||
let data = cur_editor.spillbuf.read().unwrap();
|
|
||||||
for x in data.iter() {
|
|
||||||
pxv_editor.send_cmd_obj(
|
|
||||||
ReprTree::new_leaf(
|
|
||||||
Context::parse(&self.ctx, "NestedNode"),
|
|
||||||
SingletonBuffer::<NestedNode>::new(
|
|
||||||
x.read().unwrap().clone()
|
|
||||||
).get_port().into()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// fixme: is it oc0 or old_cur ??
|
|
||||||
if oc0.tree_addr.len() > 1 {
|
|
||||||
pxv_editor.goto(TreeCursor {
|
|
||||||
tree_addr: vec![ old_cur.tree_addr[0], 0 ],
|
|
||||||
leaf_mode: ListCursorMode::Insert
|
|
||||||
});
|
|
||||||
pxv_editor.send_cmd_obj(ListCmd::DeletePxev.into_repr_tree( &self.ctx ));
|
|
||||||
} else if oc0.tree_addr.len() > 0 {
|
|
||||||
pxv_editor.goto(TreeCursor {
|
|
||||||
tree_addr: vec![ old_cur.tree_addr[0] ],
|
|
||||||
leaf_mode: ListCursorMode::Insert
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.cursor.set(ListCursor {
|
|
||||||
idx: Some(idx as isize - 1),
|
|
||||||
mode: ListCursorMode::Select
|
|
||||||
});
|
|
||||||
|
|
||||||
// remove cur_editor from top list, its elements are now in pxv_editor
|
|
||||||
self.data.remove(idx as usize);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn listlist_join_nexd(&mut self, idx: usize) {
|
|
||||||
{
|
|
||||||
let cur_editor = self.data.get(idx);
|
|
||||||
let nxd_editor = self.data.get(idx + 1);
|
|
||||||
let mut cur_editor = cur_editor.write().unwrap();
|
|
||||||
let mut nxd_editor = nxd_editor.write().unwrap();
|
|
||||||
|
|
||||||
let oc0 = cur_editor.get_cursor();
|
|
||||||
|
|
||||||
// tell next_editor move all its elements into its spill-buffer
|
|
||||||
nxd_editor.goto(TreeCursor::none());
|
|
||||||
nxd_editor.send_cmd_obj(
|
|
||||||
ListCmd::Clear.into_repr_tree( &self.ctx )
|
|
||||||
);
|
|
||||||
|
|
||||||
let old_cur = cur_editor.get_cursor();
|
|
||||||
cur_editor.goto(TreeCursor {
|
|
||||||
tree_addr: vec![-1],
|
|
||||||
leaf_mode: ListCursorMode::Insert
|
|
||||||
});
|
|
||||||
|
|
||||||
let data = nxd_editor.spillbuf.read().unwrap();
|
|
||||||
|
|
||||||
for x in data.iter() {
|
|
||||||
cur_editor.send_cmd_obj(
|
|
||||||
ReprTree::new_leaf(
|
|
||||||
Context::parse(&self.ctx, "NestedNode"),
|
|
||||||
SingletonBuffer::<NestedNode>::new(
|
|
||||||
x.read().unwrap().clone()
|
|
||||||
).get_port().into()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// fixme: is it oc0 or old_cur ??
|
|
||||||
if oc0.tree_addr.len() > 1 {
|
|
||||||
cur_editor.goto(TreeCursor {
|
|
||||||
tree_addr: vec![ old_cur.tree_addr[0], -1 ],
|
|
||||||
leaf_mode: ListCursorMode::Insert
|
|
||||||
});
|
|
||||||
cur_editor.send_cmd_obj(ListCmd::DeleteNexd.into_repr_tree( &self.ctx ));
|
|
||||||
} else if oc0.tree_addr.len() > 0 {
|
|
||||||
cur_editor.goto(TreeCursor {
|
|
||||||
tree_addr: vec![ old_cur.tree_addr[0] ],
|
|
||||||
leaf_mode: ListCursorMode::Insert
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
cur_editor.goto(TreeCursor::none());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove next_editor from top list, its elements are now in cur_editor
|
|
||||||
self.data.remove(idx+1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
use crate::{
|
|
||||||
type_system::TypeLadder,
|
|
||||||
tree::{TreeType, TreeAddr}
|
|
||||||
};
|
|
||||||
|
|
||||||
impl TreeType for ListEditor {
|
|
||||||
fn get_type(&self, addr: &TreeAddr) -> TypeLadder {
|
|
||||||
let idx = crate::utils::modulo::modulo(addr.0[0] as isize, self.data.len() as isize) as usize;
|
|
||||||
|
|
||||||
let mut addr = addr.clone();
|
|
||||||
|
|
||||||
if self.data.len() > 0 {
|
|
||||||
addr.0.remove(0);
|
|
||||||
self.data.get(idx).get_type(addr)
|
|
||||||
} else {
|
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
pub mod cursor;
|
|
||||||
pub mod editor;
|
|
||||||
pub mod nav;
|
|
||||||
pub mod segment;
|
|
||||||
pub mod pty_editor;
|
|
||||||
pub mod cmd;
|
|
||||||
pub mod ctx;
|
|
||||||
|
|
||||||
pub use {
|
|
||||||
cursor::{ListCursor, ListCursorMode},
|
|
||||||
editor::ListEditor,
|
|
||||||
segment::{ListSegment, ListSegmentSequence},
|
|
||||||
pty_editor::{PTYListStyle, PTYListController},
|
|
||||||
cmd::ListCmd,
|
|
||||||
ctx::init_ctx
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,429 +0,0 @@
|
||||||
use {
|
|
||||||
r3vi::{
|
|
||||||
view::{
|
|
||||||
OuterViewPort,
|
|
||||||
singleton::*,
|
|
||||||
sequence::*
|
|
||||||
}
|
|
||||||
},
|
|
||||||
crate::{
|
|
||||||
editors::list::{
|
|
||||||
ListCursor, ListCursorMode,
|
|
||||||
editor::ListEditor
|
|
||||||
},
|
|
||||||
tree::{TreeCursor, TreeNav, TreeNavResult, TreeHeightOp}
|
|
||||||
},
|
|
||||||
cgmath::Vector2
|
|
||||||
};
|
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
|
|
||||||
impl TreeNav for ListEditor {
|
|
||||||
fn get_addr_view(&self) -> OuterViewPort<dyn SequenceView<Item = isize>> {
|
|
||||||
self.addr_port.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_mode_view(&self) -> OuterViewPort<dyn SingletonView<Item = ListCursorMode>> {
|
|
||||||
self.mode_port.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_height(&self, op: &TreeHeightOp) -> usize {
|
|
||||||
match op {
|
|
||||||
TreeHeightOp::P | TreeHeightOp::Q => {
|
|
||||||
if self.data.len() > 0 {
|
|
||||||
1 + self.data.get(match op {
|
|
||||||
TreeHeightOp::P => 0,
|
|
||||||
TreeHeightOp::Q => self.data.len() - 1,
|
|
||||||
_ => 0
|
|
||||||
}).read().unwrap().get_height(op)
|
|
||||||
} else {
|
|
||||||
1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TreeHeightOp::Max => {
|
|
||||||
1 + (0..self.data.len() as usize)
|
|
||||||
.map(|i| self.data
|
|
||||||
.get(i).read().unwrap()
|
|
||||||
.get_height(&TreeHeightOp::Max)
|
|
||||||
)
|
|
||||||
.max()
|
|
||||||
.unwrap_or(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_cursor_warp(&self) -> TreeCursor {
|
|
||||||
let cur = self.cursor.get();
|
|
||||||
match cur.mode {
|
|
||||||
ListCursorMode::Insert => TreeCursor {
|
|
||||||
leaf_mode: cur.mode,
|
|
||||||
tree_addr: if let Some(i) = cur.idx {
|
|
||||||
vec![
|
|
||||||
i - self.data.len() as isize - 1
|
|
||||||
]
|
|
||||||
} else {
|
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ListCursorMode::Select => {
|
|
||||||
if let Some(i) = cur.idx {
|
|
||||||
if i < self.data.len() as isize {
|
|
||||||
let mut sub_cur = self.data.get(i as usize).read().unwrap().get_cursor_warp();
|
|
||||||
sub_cur.tree_addr.insert(0, i as isize - self.data.len() as isize);
|
|
||||||
return sub_cur;
|
|
||||||
} else {
|
|
||||||
return TreeCursor {
|
|
||||||
leaf_mode: ListCursorMode::Select,
|
|
||||||
tree_addr: vec![ i as isize - self.data.len() as isize ],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TreeCursor {
|
|
||||||
leaf_mode: cur.mode,
|
|
||||||
tree_addr: vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_cursor(&self) -> TreeCursor {
|
|
||||||
let cur = self.cursor.get();
|
|
||||||
match cur.mode {
|
|
||||||
ListCursorMode::Insert => TreeCursor {
|
|
||||||
leaf_mode: cur.mode,
|
|
||||||
tree_addr: if let Some(i) = cur.idx {
|
|
||||||
vec![i]
|
|
||||||
} else {
|
|
||||||
vec![]
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ListCursorMode::Select => {
|
|
||||||
if let Some(i) = cur.idx {
|
|
||||||
if i < self.data.len() as isize {
|
|
||||||
let mut sub_cur = self.data.get(i as usize).read().unwrap().get_cursor();
|
|
||||||
if sub_cur.tree_addr.len() > 0 {
|
|
||||||
sub_cur.tree_addr.insert(0, i as isize);
|
|
||||||
return sub_cur;
|
|
||||||
} else {
|
|
||||||
return TreeCursor {
|
|
||||||
leaf_mode: ListCursorMode::Select,
|
|
||||||
tree_addr: vec![ i ],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TreeCursor {
|
|
||||||
leaf_mode: ListCursorMode::Select,
|
|
||||||
tree_addr: vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn goto(&mut self, new_cur: TreeCursor) -> TreeNavResult {
|
|
||||||
let old_cur = self.cursor.get();
|
|
||||||
if let Some(i) = old_cur.idx {
|
|
||||||
if i < self.data.len() as isize {
|
|
||||||
self.data.get_mut(i as usize).write().unwrap().goto(TreeCursor::none());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match new_cur.tree_addr.len() {
|
|
||||||
0 => {
|
|
||||||
self.cursor.set(ListCursor {
|
|
||||||
mode: new_cur.leaf_mode,
|
|
||||||
idx: None,
|
|
||||||
});
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
1 => {
|
|
||||||
let idx = crate::utils::modulo(new_cur.tree_addr[0], if new_cur.leaf_mode == ListCursorMode::Insert { 1 } else { 0 } + self.data.len() as isize);
|
|
||||||
|
|
||||||
self.cursor.set(ListCursor {
|
|
||||||
mode: new_cur.leaf_mode,
|
|
||||||
idx: Some(idx),
|
|
||||||
});
|
|
||||||
|
|
||||||
if new_cur.leaf_mode == ListCursorMode::Select && self.data.len() > 0 {
|
|
||||||
self.data
|
|
||||||
.get_mut(idx as usize)
|
|
||||||
.write().unwrap()
|
|
||||||
.goto(TreeCursor {
|
|
||||||
leaf_mode: ListCursorMode::Select,
|
|
||||||
tree_addr: vec![]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
if self.data.len() > 0 {
|
|
||||||
let idx = crate::utils::modulo(new_cur.tree_addr[0], self.data.len() as isize);
|
|
||||||
|
|
||||||
self.cursor.set(ListCursor {
|
|
||||||
mode: ListCursorMode::Select,
|
|
||||||
idx: Some(idx),
|
|
||||||
});
|
|
||||||
|
|
||||||
self.data
|
|
||||||
.get_mut(idx as usize)
|
|
||||||
.write().unwrap()
|
|
||||||
.goto(TreeCursor {
|
|
||||||
leaf_mode: new_cur.leaf_mode,
|
|
||||||
tree_addr: new_cur.tree_addr[1..].iter().cloned().collect(),
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
self.cursor.set(ListCursor::home());
|
|
||||||
}
|
|
||||||
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn goby(&mut self, direction: Vector2<isize>) -> TreeNavResult {
|
|
||||||
let mut cur = self.get_cursor();
|
|
||||||
|
|
||||||
let gravity = true;
|
|
||||||
|
|
||||||
match cur.tree_addr.len() {
|
|
||||||
0 => {
|
|
||||||
|
|
||||||
if direction.y < 0 {
|
|
||||||
// up
|
|
||||||
self.cursor.set(ListCursor::none());
|
|
||||||
TreeNavResult::Exit
|
|
||||||
} else if direction.y > 0 {
|
|
||||||
// dn
|
|
||||||
eprintln!("dn: data.len() = {}", self.data.len());
|
|
||||||
self.cursor.set(ListCursor {
|
|
||||||
mode: if self.data.len() > 0 { cur.leaf_mode } else { ListCursorMode::Insert },
|
|
||||||
idx: Some(0)
|
|
||||||
});
|
|
||||||
|
|
||||||
self.goby(Vector2::new(direction.x, direction.y-1));
|
|
||||||
TreeNavResult::Continue
|
|
||||||
} else {
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
1 => {
|
|
||||||
if direction.y > 0 {
|
|
||||||
// dn
|
|
||||||
|
|
||||||
if cur.tree_addr[0] < self.data.len() as isize {
|
|
||||||
if self.data
|
|
||||||
.get_mut(cur.tree_addr[0] as usize)
|
|
||||||
.write().unwrap()
|
|
||||||
.goby(Vector2::new(direction.x, direction.y))
|
|
||||||
== TreeNavResult::Continue {
|
|
||||||
self.cursor.set(ListCursor {
|
|
||||||
mode: ListCursorMode::Select,
|
|
||||||
idx: Some(cur.tree_addr[0])
|
|
||||||
});
|
|
||||||
self.set_leaf_mode(cur.leaf_mode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TreeNavResult::Continue
|
|
||||||
|
|
||||||
} else if direction.y < 0 {
|
|
||||||
// up
|
|
||||||
self.cursor.set(ListCursor {
|
|
||||||
mode: cur.leaf_mode,
|
|
||||||
idx: None
|
|
||||||
});
|
|
||||||
TreeNavResult::Exit
|
|
||||||
} else {
|
|
||||||
// horizontal
|
|
||||||
|
|
||||||
if (cur.tree_addr[0]+direction.x >= 0) &&
|
|
||||||
(cur.tree_addr[0]+direction.x <
|
|
||||||
self.data.len() as isize
|
|
||||||
+ if cur.leaf_mode == ListCursorMode::Insert { 1 } else { 0 })
|
|
||||||
{
|
|
||||||
let idx = cur.tree_addr[0] + direction.x;
|
|
||||||
let mut new_addr = Vec::new();
|
|
||||||
|
|
||||||
match cur.leaf_mode {
|
|
||||||
ListCursorMode::Select => {
|
|
||||||
let cur_item = self.data.get(cur.tree_addr[0] as usize);
|
|
||||||
let cur_height = cur_item.read().unwrap().get_height(&TreeHeightOp::Max);
|
|
||||||
|
|
||||||
let new_item = self.data
|
|
||||||
.get_mut(idx as usize);
|
|
||||||
|
|
||||||
let height = new_item.read().unwrap().get_height(
|
|
||||||
if direction.x < 0 {
|
|
||||||
&TreeHeightOp::Q
|
|
||||||
} else {
|
|
||||||
&TreeHeightOp::P
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
new_addr.push(idx);
|
|
||||||
if gravity && cur_height < 2 {
|
|
||||||
for _ in 1..height {
|
|
||||||
new_addr.push( if direction.x >= 0 {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
-1
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ListCursorMode::Insert => {
|
|
||||||
let gravity = false;
|
|
||||||
if direction.x > 0
|
|
||||||
{
|
|
||||||
if (cur.tree_addr[0] as usize) < self.data.len() {
|
|
||||||
let cur_item = self.data.get(cur.tree_addr[0] as usize);
|
|
||||||
let cur_height = cur_item.read().unwrap().get_height(&TreeHeightOp::P);
|
|
||||||
|
|
||||||
if gravity && cur_height > 1 {
|
|
||||||
new_addr.push( cur.tree_addr[0] );
|
|
||||||
new_addr.push(0);
|
|
||||||
} else {
|
|
||||||
new_addr.push( idx );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (idx as usize) < self.data.len() {
|
|
||||||
let pxv_item = self.data.get(idx as usize);
|
|
||||||
let pxv_height = pxv_item.read().unwrap().get_height(&TreeHeightOp::P);
|
|
||||||
|
|
||||||
if gravity && pxv_height > 1 {
|
|
||||||
new_addr.push( idx );
|
|
||||||
new_addr.push( -1 );
|
|
||||||
} else {
|
|
||||||
new_addr.push( idx );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.data.len() == 0 {
|
|
||||||
cur.leaf_mode = ListCursorMode::Insert
|
|
||||||
}
|
|
||||||
cur.tree_addr = new_addr;
|
|
||||||
self.goto(cur);
|
|
||||||
|
|
||||||
TreeNavResult::Continue
|
|
||||||
} else {
|
|
||||||
self.cursor.set(ListCursor {
|
|
||||||
mode: cur.leaf_mode,
|
|
||||||
idx: None
|
|
||||||
});
|
|
||||||
self.cursor.set(ListCursor::none());
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
depth => {
|
|
||||||
// nested
|
|
||||||
|
|
||||||
if cur.tree_addr[0] < self.data.len() as isize {
|
|
||||||
|
|
||||||
let cur_item = self.data
|
|
||||||
.get_mut(cur.tree_addr[0] as usize);
|
|
||||||
|
|
||||||
let result = cur_item.write().unwrap().goby(direction);
|
|
||||||
|
|
||||||
match result
|
|
||||||
{
|
|
||||||
TreeNavResult::Exit => {
|
|
||||||
if direction.y < 0 {
|
|
||||||
// up
|
|
||||||
self.cursor.set(ListCursor {
|
|
||||||
mode: cur.leaf_mode,
|
|
||||||
idx: Some(cur.tree_addr[0])
|
|
||||||
});
|
|
||||||
|
|
||||||
TreeNavResult::Continue
|
|
||||||
} else if direction.y > 0 {
|
|
||||||
// dn
|
|
||||||
|
|
||||||
TreeNavResult::Continue
|
|
||||||
} else {
|
|
||||||
// horizontal
|
|
||||||
if (cur.tree_addr[0]+direction.x >= 0) &&
|
|
||||||
(cur.tree_addr[0]+direction.x < self.data.len() as isize)
|
|
||||||
{
|
|
||||||
let mut new_addr = Vec::new();
|
|
||||||
|
|
||||||
if direction.x < 0 {
|
|
||||||
let pxv_item = self.data
|
|
||||||
.get_mut(cur.tree_addr[0] as usize - 1);
|
|
||||||
|
|
||||||
let pxv_height = pxv_item.read().unwrap().get_height(&TreeHeightOp::Q) as isize;
|
|
||||||
let cur_height = cur_item.read().unwrap().get_height(&TreeHeightOp::P) as isize;
|
|
||||||
let dist_from_ground = cur_height - (depth as isize - 1);
|
|
||||||
let n_steps_down =
|
|
||||||
if gravity {
|
|
||||||
pxv_height - dist_from_ground
|
|
||||||
} else {
|
|
||||||
depth as isize - 1
|
|
||||||
};
|
|
||||||
|
|
||||||
eprintln!("<- LEFT CROSS: pxv_height = {}, cur_height = {}, dist_from_ground = {}, n_steps_down = {}", pxv_height, cur_height, dist_from_ground, n_steps_down);
|
|
||||||
new_addr.push( cur.tree_addr[0] - 1 );
|
|
||||||
for _i in 0..n_steps_down {
|
|
||||||
new_addr.push( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
let nxd_item = self.data
|
|
||||||
.get_mut(cur.tree_addr[0] as usize + 1);
|
|
||||||
|
|
||||||
let cur_height = cur_item.read().unwrap().get_height(&TreeHeightOp::Q) as isize;
|
|
||||||
let nxd_height = nxd_item.read().unwrap().get_height(&TreeHeightOp::P) as isize;
|
|
||||||
let dist_from_ground = cur_height - (depth as isize - 1);
|
|
||||||
let n_steps_down =
|
|
||||||
if gravity {
|
|
||||||
nxd_height - dist_from_ground
|
|
||||||
} else {
|
|
||||||
depth as isize - 1
|
|
||||||
};
|
|
||||||
|
|
||||||
eprintln!("-> RIGHT CROSS: cur_height = {}, nxd_height = {}, dist_from_ground = {}, n_steps_down = {}", cur_height, nxd_height, dist_from_ground, n_steps_down);
|
|
||||||
new_addr.push( cur.tree_addr[0] + 1 );
|
|
||||||
for _i in 0..n_steps_down {
|
|
||||||
new_addr.push( 0 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
drop(cur_item);
|
|
||||||
|
|
||||||
eprintln!("CROSS: goto {:?}", new_addr);
|
|
||||||
cur.tree_addr = new_addr;
|
|
||||||
self.goto(cur)
|
|
||||||
} else {
|
|
||||||
self.cursor.set(ListCursor {
|
|
||||||
mode: cur.leaf_mode,
|
|
||||||
idx: None
|
|
||||||
});
|
|
||||||
self.cursor.set(ListCursor::none());
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TreeNavResult::Continue => TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} else {
|
|
||||||
self.cursor.set(
|
|
||||||
ListCursor {
|
|
||||||
mode: ListCursorMode::Insert,
|
|
||||||
idx: Some(0)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,250 +0,0 @@
|
||||||
use {
|
|
||||||
r3vi::{
|
|
||||||
view::{ViewPort, OuterViewPort, sequence::*},
|
|
||||||
projection::decorate_sequence::*,
|
|
||||||
},
|
|
||||||
crate::{
|
|
||||||
type_system::{Context, ReprTree},
|
|
||||||
editors::list::*,
|
|
||||||
terminal::{TerminalEvent, TerminalView, make_label},
|
|
||||||
tree::{TreeCursor, TreeNav, TreeNavResult},
|
|
||||||
tree::NestedNode,
|
|
||||||
PtySegment
|
|
||||||
},
|
|
||||||
std::sync::{Arc, RwLock},
|
|
||||||
termion::event::{Event, Key}
|
|
||||||
};
|
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
|
|
||||||
pub struct PTYListStyle {
|
|
||||||
style: (String, String, String)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PTYListStyle {
|
|
||||||
pub fn new(style: (&str, &str, &str)) -> PTYListStyle {
|
|
||||||
PTYListStyle {
|
|
||||||
style: (style.0.into(), style.1.into(), style.2.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_seg_seq_view(&self, editor: &ListEditor) -> OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>> {
|
|
||||||
let seg_seq = ListSegmentSequence::new(
|
|
||||||
editor.get_cursor_port(),
|
|
||||||
editor.get_data_port()
|
|
||||||
);
|
|
||||||
let se = seg_seq.read().unwrap();
|
|
||||||
se.get_view().map(move |segment| segment.pty_view())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pty_view(&self, editor: &ListEditor) -> OuterViewPort<dyn TerminalView> {
|
|
||||||
let seg_seq = ListSegmentSequence::new(
|
|
||||||
editor.get_cursor_port(),
|
|
||||||
editor.get_data_port()
|
|
||||||
);
|
|
||||||
let seg_seq = seg_seq.read().unwrap();
|
|
||||||
|
|
||||||
seg_seq
|
|
||||||
.get_view()
|
|
||||||
.map(move |segment| segment.pty_view())
|
|
||||||
.separate(make_label(&self.style.1))
|
|
||||||
.wrap(make_label(&self.style.0), make_label(&self.style.2))
|
|
||||||
.to_grid_horizontal()
|
|
||||||
.flatten()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn for_node(node: &mut NestedNode, style: (&str, &str, &str)) {
|
|
||||||
node.view = Some(
|
|
||||||
Self::new(style)
|
|
||||||
.pty_view(
|
|
||||||
&node.get_edit::<ListEditor>().unwrap().read().unwrap()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
|
|
||||||
// todo: rename to CharController
|
|
||||||
|
|
||||||
pub struct PTYListController {
|
|
||||||
pub editor: Arc<RwLock<ListEditor>>,
|
|
||||||
|
|
||||||
split_char: Option<char>,
|
|
||||||
close_char: Option<char>,
|
|
||||||
|
|
||||||
depth: OuterViewPort<dyn SingletonView<Item = usize>>
|
|
||||||
}
|
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
|
|
||||||
impl PTYListController {
|
|
||||||
pub fn from_editor(
|
|
||||||
editor: Arc<RwLock<ListEditor>>,
|
|
||||||
split_char: Option<char>,
|
|
||||||
close_char: Option<char>,
|
|
||||||
depth: OuterViewPort<dyn SingletonView<Item = usize>>
|
|
||||||
) -> Self {
|
|
||||||
PTYListController {
|
|
||||||
editor,
|
|
||||||
split_char,
|
|
||||||
close_char,
|
|
||||||
depth
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn for_node(
|
|
||||||
node: &mut NestedNode,
|
|
||||||
split_char: Option<char>,
|
|
||||||
close_char: Option<char>
|
|
||||||
) {
|
|
||||||
{
|
|
||||||
let ctx = node.ctx.as_ref();
|
|
||||||
let mut ctx = ctx.write().unwrap();
|
|
||||||
|
|
||||||
if let Some(c) = split_char.as_ref() {
|
|
||||||
ctx.meta_chars.push(*c);
|
|
||||||
}
|
|
||||||
if let Some(c) = close_char.as_ref() {
|
|
||||||
ctx.meta_chars.push(*c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let editor = node.get_edit::<ListEditor>().unwrap();
|
|
||||||
let controller = Arc::new(RwLock::new(PTYListController::from_editor( editor, split_char, close_char, node.depth.clone() )));
|
|
||||||
|
|
||||||
node.cmd.set(Some(controller.clone()));
|
|
||||||
node.close_char.set(close_char);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_data_port(&self) -> OuterViewPort<dyn SequenceView<Item = NestedNode>> {
|
|
||||||
self.editor.read().unwrap().get_data_port()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
self.editor.write().unwrap().clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_item(&self) -> Option<NestedNode> {
|
|
||||||
self.editor.read().unwrap().get_item()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_term_event(&mut self, event: &TerminalEvent, _cmd_obj: Arc<RwLock<ReprTree>>) -> TreeNavResult {
|
|
||||||
let mut e = self.editor.write().unwrap();
|
|
||||||
match event {
|
|
||||||
TerminalEvent::Input(Event::Key(Key::Insert)) => {
|
|
||||||
e.toggle_leaf_mode();
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
_ => TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_meta_char(&mut self, c: char, child_close_char: Option<char>) -> TreeNavResult {
|
|
||||||
eprintln!("handle meta char: got '{}', child_close={:?}, self.close={:?}, split={:?}", c, child_close_char, self.close_char, self.split_char);
|
|
||||||
let mut e = self.editor.write().unwrap();
|
|
||||||
let cur = e.cursor.get();
|
|
||||||
|
|
||||||
if Some(c) == self.split_char
|
|
||||||
// || Some(c) == child_close_char
|
|
||||||
{
|
|
||||||
e.listlist_split();
|
|
||||||
eprintln!("done listlist split");
|
|
||||||
TreeNavResult::Continue
|
|
||||||
} else if Some(c) == child_close_char {
|
|
||||||
e.goto(TreeCursor::none());
|
|
||||||
e.cursor.set(ListCursor {
|
|
||||||
mode: ListCursorMode::Insert,
|
|
||||||
idx: Some(cur.idx.unwrap_or(0)+1)
|
|
||||||
});
|
|
||||||
TreeNavResult::Continue
|
|
||||||
} else {
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_any_event(&mut self, cmd_obj: Arc<RwLock<ReprTree>>) -> TreeNavResult {
|
|
||||||
let mut e = self.editor.write().unwrap();
|
|
||||||
let cur = e.cursor.get();
|
|
||||||
let ctx = e.ctx.clone();
|
|
||||||
let ctx = ctx.read().unwrap();
|
|
||||||
|
|
||||||
match cur.mode {
|
|
||||||
ListCursorMode::Insert => {
|
|
||||||
let mut new_edit = Context::make_node(&e.ctx, e.typ.clone(), self.depth.map(|d| d+1)).unwrap();
|
|
||||||
new_edit.goto(TreeCursor::home());
|
|
||||||
|
|
||||||
match new_edit.send_cmd_obj(cmd_obj.clone()) {
|
|
||||||
TreeNavResult::Continue => {
|
|
||||||
e.insert(Arc::new(RwLock::new(new_edit.clone())));
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
TreeNavResult::Exit => {
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ListCursorMode::Select => {
|
|
||||||
if let Some(item) = e.get_item_mut() {
|
|
||||||
let res = item.write().unwrap().send_cmd_obj(cmd_obj.clone());
|
|
||||||
let child_close_char = item.read().unwrap().close_char.get();
|
|
||||||
|
|
||||||
match res {
|
|
||||||
TreeNavResult::Continue => TreeNavResult::Continue,
|
|
||||||
TreeNavResult::Exit => {
|
|
||||||
// child editor returned control, probably for meta-char handling..
|
|
||||||
|
|
||||||
if cmd_obj.read().unwrap().get_type().clone() == ctx.type_term_from_str("Char").unwrap() {
|
|
||||||
let co = cmd_obj.read().unwrap();
|
|
||||||
if let Some(cmd_view) = co.get_view::<dyn SingletonView<Item = char>>() {
|
|
||||||
drop(co);
|
|
||||||
drop(e);
|
|
||||||
self.handle_meta_char(cmd_view.get(), child_close_char)
|
|
||||||
} else {
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// cursor selects non existent item
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
use r3vi::view::singleton::SingletonView;
|
|
||||||
use crate::commander::ObjCommander;
|
|
||||||
|
|
||||||
impl ObjCommander for PTYListController {
|
|
||||||
fn send_cmd_obj(&mut self, cmd_obj: Arc<RwLock<ReprTree>>) -> TreeNavResult {
|
|
||||||
let mut e = self.editor.write().unwrap();
|
|
||||||
let cmd_type = cmd_obj.read().unwrap().get_type().clone();
|
|
||||||
|
|
||||||
if cmd_type == Context::parse(&e.ctx, "ListCmd").into()
|
|
||||||
|| cmd_type == Context::parse(&e.ctx, "NestedNode").into()
|
|
||||||
{
|
|
||||||
e.send_cmd_obj( cmd_obj )
|
|
||||||
}
|
|
||||||
|
|
||||||
else if cmd_type == Context::parse(&e.ctx, "TerminalEvent").into() {
|
|
||||||
let co = cmd_obj.read().unwrap();
|
|
||||||
if let Some(view) = co.get_view::<dyn SingletonView<Item = TerminalEvent>>() {
|
|
||||||
drop( co );
|
|
||||||
drop( e );
|
|
||||||
self.handle_term_event( &view.get(), cmd_obj )
|
|
||||||
} else {
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
drop( e );
|
|
||||||
self.handle_any_event( cmd_obj )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,174 +0,0 @@
|
||||||
use {
|
|
||||||
r3vi::{
|
|
||||||
view::{
|
|
||||||
Observer, ObserverBroadcast, ObserverExt, OuterViewPort, View, ViewPort,
|
|
||||||
singleton::*,
|
|
||||||
sequence::*,
|
|
||||||
},
|
|
||||||
projection::projection_helper::*
|
|
||||||
},
|
|
||||||
crate::{
|
|
||||||
editors::list::{ListCursor, ListCursorMode},
|
|
||||||
terminal::{TerminalView, TerminalStyle, make_label},
|
|
||||||
tree::{NestedNode, TreeNav},
|
|
||||||
utils::color::{bg_style_from_depth, fg_style_from_depth},
|
|
||||||
PtySegment
|
|
||||||
},
|
|
||||||
std::sync::Arc,
|
|
||||||
std::sync::RwLock,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub enum ListSegment {
|
|
||||||
InsertCursor,
|
|
||||||
Item {
|
|
||||||
editor: NestedNode,
|
|
||||||
cur_dist: isize,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PtySegment for ListSegment {
|
|
||||||
fn pty_view(&self) -> OuterViewPort<dyn TerminalView> {
|
|
||||||
match self {
|
|
||||||
ListSegment::InsertCursor => {
|
|
||||||
make_label("|")
|
|
||||||
.map_item(move |_pt, atom| {
|
|
||||||
atom.add_style_front(TerminalStyle::fg_color((150,80,230)))
|
|
||||||
.add_style_front(TerminalStyle::bold(true))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
ListSegment::Item{ editor, cur_dist } => {
|
|
||||||
let e = editor.clone();
|
|
||||||
let cur_dist = *cur_dist;
|
|
||||||
editor.get_view().map_item(move |_pt, atom| {
|
|
||||||
let c = e.get_cursor();
|
|
||||||
let cur_depth = c.tree_addr.len();
|
|
||||||
let select =
|
|
||||||
if cur_dist == 0 {
|
|
||||||
cur_depth
|
|
||||||
} else {
|
|
||||||
usize::MAX
|
|
||||||
};
|
|
||||||
|
|
||||||
atom
|
|
||||||
.add_style_back(bg_style_from_depth(select))
|
|
||||||
.add_style_back(TerminalStyle::bold(select==1))
|
|
||||||
.add_style_back(fg_style_from_depth(e.depth.get_view().get()))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ListSegmentSequence {
|
|
||||||
data: Arc<dyn SequenceView<Item = NestedNode>>,
|
|
||||||
cursor: Arc<dyn SingletonView<Item = ListCursor>>,
|
|
||||||
|
|
||||||
cur_cursor: ListCursor,
|
|
||||||
|
|
||||||
port: ViewPort<dyn SequenceView<Item = ListSegment>>,
|
|
||||||
cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = ListSegment>>>>,
|
|
||||||
proj_helper: ProjectionHelper<usize, Self>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl View for ListSegmentSequence {
|
|
||||||
type Msg = usize;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SequenceView for ListSegmentSequence {
|
|
||||||
type Item = ListSegment;
|
|
||||||
|
|
||||||
fn len(&self) -> Option<usize> {
|
|
||||||
match self.cur_cursor.mode {
|
|
||||||
ListCursorMode::Insert => {
|
|
||||||
Some(self.data.len()? + if self.cur_cursor.idx.is_some() { 1 } else { 0 })
|
|
||||||
}
|
|
||||||
_ => self.data.len(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get(&self, idx: &usize) -> Option<Self::Item> {
|
|
||||||
let c = self.cursor.get();
|
|
||||||
Some(if let Some(cur) = c.idx {
|
|
||||||
match c.mode {
|
|
||||||
ListCursorMode::Select => {
|
|
||||||
ListSegment::Item {
|
|
||||||
editor: self.data.get(idx)?,
|
|
||||||
cur_dist: cur - *idx as isize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ListCursorMode::Insert => {
|
|
||||||
if *idx < cur as usize {
|
|
||||||
ListSegment::Item {
|
|
||||||
editor: self.data.get(idx)?,
|
|
||||||
cur_dist: cur - *idx as isize
|
|
||||||
}
|
|
||||||
} else if *idx == cur as usize {
|
|
||||||
ListSegment::InsertCursor
|
|
||||||
} else {
|
|
||||||
ListSegment::Item {
|
|
||||||
editor: self.data.get(&(*idx - 1))?,
|
|
||||||
cur_dist: cur - *idx as isize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ListSegment::Item {
|
|
||||||
editor: self.data.get(&idx)?,
|
|
||||||
cur_dist: *idx as isize + 1
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ListSegmentSequence {
|
|
||||||
pub fn new(
|
|
||||||
cursor_port: OuterViewPort<dyn SingletonView<Item = ListCursor>>,
|
|
||||||
data_port: OuterViewPort<dyn SequenceView<Item = NestedNode>>,
|
|
||||||
) -> Arc<RwLock<Self>> {
|
|
||||||
let out_port = ViewPort::new();
|
|
||||||
let mut proj_helper = ProjectionHelper::new(out_port.update_hooks.clone());
|
|
||||||
let proj = Arc::new(RwLock::new(ListSegmentSequence {
|
|
||||||
cur_cursor: cursor_port.get_view().get(),
|
|
||||||
port: out_port.clone(),
|
|
||||||
|
|
||||||
cursor: proj_helper.new_singleton_arg(0, cursor_port, |s: &mut Self, _msg| {
|
|
||||||
let _old_cursor = s.cur_cursor;
|
|
||||||
s.cur_cursor = s.cursor.get();
|
|
||||||
|
|
||||||
// todo: optimize
|
|
||||||
s.cast.notify_each(0..=s.data.len().unwrap_or(0) + 1);
|
|
||||||
}),
|
|
||||||
|
|
||||||
data: proj_helper.new_sequence_arg(1, data_port, |s: &mut Self, idx| {
|
|
||||||
if let Some(cur_idx) = s.cur_cursor.idx {
|
|
||||||
match s.cur_cursor.mode {
|
|
||||||
ListCursorMode::Insert => {
|
|
||||||
if *idx < cur_idx as usize {
|
|
||||||
s.cast.notify(idx);
|
|
||||||
} else {
|
|
||||||
s.cast.notify(&(*idx + 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
s.cast.notify(idx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
s.cast.notify(idx);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
cast: out_port.inner().get_broadcast(),
|
|
||||||
proj_helper,
|
|
||||||
}));
|
|
||||||
|
|
||||||
proj.write().unwrap().proj_helper.set_proj(&proj);
|
|
||||||
out_port.inner().set_view(Some(proj.clone()));
|
|
||||||
|
|
||||||
proj
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_view(&self) -> OuterViewPort<dyn SequenceView<Item = ListSegment>> {
|
|
||||||
self.port.outer()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
|
|
||||||
pub mod list;
|
|
||||||
pub mod product;
|
|
||||||
pub mod sum;
|
|
||||||
|
|
||||||
pub mod char;
|
|
||||||
pub mod integer;
|
|
||||||
pub mod typeterm;
|
|
||||||
|
|
|
@ -1,280 +0,0 @@
|
||||||
use {
|
|
||||||
r3vi::{
|
|
||||||
view::{
|
|
||||||
OuterViewPort,
|
|
||||||
sequence::*
|
|
||||||
},
|
|
||||||
buffer::{
|
|
||||||
vec::*,
|
|
||||||
index_hashmap::*
|
|
||||||
}
|
|
||||||
},
|
|
||||||
laddertypes::{TypeTerm},
|
|
||||||
crate::{
|
|
||||||
type_system::{Context},
|
|
||||||
terminal::{
|
|
||||||
TerminalEditor, TerminalEditorResult,
|
|
||||||
TerminalEvent, TerminalView
|
|
||||||
},
|
|
||||||
editors::{
|
|
||||||
list::ListCursorMode,
|
|
||||||
product::{
|
|
||||||
segment::ProductEditorSegment
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tree::{TreeNav, TreeNavResult},
|
|
||||||
diagnostics::{Diagnostics},
|
|
||||||
terminal::{TerminalStyle},
|
|
||||||
tree::NestedNode
|
|
||||||
},
|
|
||||||
cgmath::{Point2},
|
|
||||||
std::sync::{Arc, RwLock},
|
|
||||||
termion::event::{Event, Key},
|
|
||||||
std::ops::{DerefMut}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct ProductEditor {
|
|
||||||
msg_buf: VecBuffer<Option<OuterViewPort<dyn SequenceView<Item = crate::diagnostics::Message>>>>,
|
|
||||||
msg_port: OuterViewPort<dyn SequenceView<Item = crate::diagnostics::Message>>,
|
|
||||||
segments: IndexBuffer<Point2<i16>, ProductEditorSegment>,
|
|
||||||
pub(super) n_indices: Vec<Point2<i16>>,
|
|
||||||
|
|
||||||
pub(super) ctx: Arc<RwLock<Context>>,
|
|
||||||
pub(super) cursor: Option<isize>,
|
|
||||||
pub(super) depth: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ProductEditor {
|
|
||||||
pub fn new(depth: usize, ctx: Arc<RwLock<Context>>) -> Self {
|
|
||||||
let msg_buf = VecBuffer::new();
|
|
||||||
ProductEditor {
|
|
||||||
segments: IndexBuffer::new(),
|
|
||||||
msg_port: msg_buf.get_port()
|
|
||||||
.to_sequence()
|
|
||||||
.enumerate()
|
|
||||||
.filter_map(|(idx, msgs): &(usize, Option<OuterViewPort<dyn SequenceView<Item = crate::diagnostics::Message>>>)| {
|
|
||||||
let idx = *idx;
|
|
||||||
if let Some(msgs) = msgs {
|
|
||||||
Some(msgs.map(
|
|
||||||
move |msg| {
|
|
||||||
let mut msg = msg.clone();
|
|
||||||
msg.addr.insert(0, idx);
|
|
||||||
msg
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.flatten(),
|
|
||||||
msg_buf,
|
|
||||||
|
|
||||||
n_indices: Vec::new(),
|
|
||||||
ctx,
|
|
||||||
cursor: None,
|
|
||||||
depth
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_t(mut self, pos: Point2<i16>, t: &str) -> Self {
|
|
||||||
self.segments.insert(pos, ProductEditorSegment::T(t.to_string(), self.depth));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_n(mut self, pos: Point2<i16>, n: &TypeTerm) -> Self {
|
|
||||||
self.segments.insert(pos, ProductEditorSegment::N{
|
|
||||||
t: n.clone(),
|
|
||||||
editor: None,
|
|
||||||
ed_depth: self.depth + 1,
|
|
||||||
cur_depth: 0,
|
|
||||||
cur_dist: isize::MAX
|
|
||||||
});
|
|
||||||
self.n_indices.push(pos);
|
|
||||||
|
|
||||||
let mut b = VecBuffer::new();
|
|
||||||
b.push(crate::diagnostics::make_todo(crate::terminal::make_label(&format!("complete {}", self.ctx.read().unwrap().type_term_to_str(n)))));
|
|
||||||
self.msg_buf.push(Some(b.get_port().to_sequence()));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_editor_segment(&self, mut idx: isize) -> ProductEditorSegment {
|
|
||||||
idx = crate::utils::modulo(idx, self.n_indices.len() as isize);
|
|
||||||
if let Some(pos) = self.n_indices.get(idx as usize) {
|
|
||||||
self.segments.get(pos).unwrap()
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_editor_segment_mut(&mut self, mut idx: isize) -> MutableIndexAccess<Point2<i16>, ProductEditorSegment> {
|
|
||||||
idx = crate::utils::modulo(idx, self.n_indices.len() as isize);
|
|
||||||
if let Some(pos) = self.n_indices.get(idx as usize) {
|
|
||||||
self.segments.get_mut(pos)
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_cur_segment(&self) -> Option<ProductEditorSegment> {
|
|
||||||
Some(self.get_editor_segment(self.cursor?))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_cur_segment_mut(&mut self) -> Option<MutableIndexAccess<Point2<i16>, ProductEditorSegment>> {
|
|
||||||
Some(self.get_editor_segment_mut(self.cursor?))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_editor(&self, idx: isize) -> Option<NestedNode> {
|
|
||||||
if let ProductEditorSegment::N{ t: _, editor, ed_depth: _, cur_depth: _, cur_dist: _ } = self.get_editor_segment(idx) {
|
|
||||||
editor
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_cur_editor(&self) -> Option<NestedNode> {
|
|
||||||
self.get_editor(self.cursor?)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_leaf_mode(&mut self, mode: ListCursorMode) {
|
|
||||||
let mut c = self.get_cursor();
|
|
||||||
c.leaf_mode = mode;
|
|
||||||
self.goto(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_segment(&mut self, idx: isize) {
|
|
||||||
if let Some(ProductEditorSegment::N{ t, editor, ed_depth: _, cur_depth, cur_dist }) = self.get_editor_segment_mut(idx).deref_mut() {
|
|
||||||
let cur = self.get_cursor();
|
|
||||||
|
|
||||||
if cur.tree_addr.len() > 0 {
|
|
||||||
if cur.tree_addr[0] == idx {
|
|
||||||
*cur_depth = cur.tree_addr.len();
|
|
||||||
}
|
|
||||||
|
|
||||||
*cur_dist = cur.tree_addr[0] - idx
|
|
||||||
} else {
|
|
||||||
*cur_dist = isize::MAX;
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(e) = editor {
|
|
||||||
self.msg_buf.update(idx as usize, Some(e.get_msg_port()));
|
|
||||||
} else {
|
|
||||||
let mut b = VecBuffer::new();
|
|
||||||
b.push(crate::diagnostics::make_todo(crate::terminal::make_label(&format!("complete {}", self.ctx.read().unwrap().type_term_to_str(&t)))));
|
|
||||||
|
|
||||||
self.msg_buf.update(idx as usize, Some(b.get_port().to_sequence()));
|
|
||||||
|
|
||||||
if cur.tree_addr.len() > 0 {
|
|
||||||
if cur.tree_addr[0] == idx {
|
|
||||||
self.msg_buf.update(idx as usize, Some(b.get_port().to_sequence().map(
|
|
||||||
|msg| {
|
|
||||||
let mut msg = msg.clone();
|
|
||||||
msg.port = msg.port.map_item(|_p,a| a.add_style_back(TerminalStyle::bg_color((40,40,40))));
|
|
||||||
msg
|
|
||||||
}
|
|
||||||
)));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_cur_segment(&mut self) {
|
|
||||||
if let Some(c) = self.cursor {
|
|
||||||
self.update_segment(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_term_view(&self) -> OuterViewPort<dyn TerminalView> {
|
|
||||||
let ctx = self.ctx.clone();
|
|
||||||
self.segments
|
|
||||||
.get_port()
|
|
||||||
.map_item(move |_pos, e: &ProductEditorSegment| { e.get_view(ctx.clone()) })
|
|
||||||
.flatten()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
use r3vi::view::singleton::SingletonView;
|
|
||||||
use crate::{commander::ObjCommander, type_system::ReprTree};
|
|
||||||
|
|
||||||
impl ObjCommander for ProductEditor {
|
|
||||||
fn send_cmd_obj(&mut self, cmd_obj: Arc<RwLock<ReprTree>>) -> TreeNavResult {
|
|
||||||
let co = cmd_obj.read().unwrap();
|
|
||||||
let cmd_type = co.get_type().clone();
|
|
||||||
let term_event_type = Context::parse(&self.ctx, "TerminalEvent").into();
|
|
||||||
|
|
||||||
if cmd_type == term_event_type {
|
|
||||||
if let Some(te_view) = co.get_view::<dyn SingletonView<Item = TerminalEvent>>() {
|
|
||||||
drop(co);
|
|
||||||
let event = te_view.get();
|
|
||||||
|
|
||||||
let mut update_segment = false;
|
|
||||||
|
|
||||||
let _result = if let Some(mut segment) = self.get_cur_segment_mut().as_deref_mut() {
|
|
||||||
if let Some(ProductEditorSegment::N{ t, editor, ed_depth, cur_depth, cur_dist: _ }) = segment.deref_mut() {
|
|
||||||
*cur_depth = self.get_cursor().tree_addr.len();
|
|
||||||
|
|
||||||
if let Some(mut e) = editor.clone() {
|
|
||||||
match e.handle_terminal_event(&event) {
|
|
||||||
TerminalEditorResult::Exit =>
|
|
||||||
match event {
|
|
||||||
TerminalEvent::Input(Event::Key(Key::Backspace)) => {
|
|
||||||
*editor = None;
|
|
||||||
update_segment = true;
|
|
||||||
TerminalEditorResult::Continue
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
*cur_depth = e.get_cursor().tree_addr.len();
|
|
||||||
match self.nexd() {
|
|
||||||
TreeNavResult::Continue => TerminalEditorResult::Continue,
|
|
||||||
TreeNavResult::Exit => TerminalEditorResult::Exit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
TerminalEditorResult::Continue => {
|
|
||||||
*cur_depth = e.get_cursor().tree_addr.len();
|
|
||||||
TerminalEditorResult::Continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let mut e = Context::make_node(&self.ctx, t.clone(), r3vi::buffer::singleton::SingletonBuffer::new(*ed_depth).get_port()).unwrap();
|
|
||||||
*editor = Some(e.clone());
|
|
||||||
update_segment = true;
|
|
||||||
|
|
||||||
e.dn();
|
|
||||||
let x = e.handle_terminal_event(&event);
|
|
||||||
x
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TerminalEditorResult::Exit
|
|
||||||
};
|
|
||||||
|
|
||||||
if update_segment {
|
|
||||||
self.update_cur_segment();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TreeNavResult::Continue
|
|
||||||
} else {
|
|
||||||
drop(co);
|
|
||||||
if let Some(mut node) = self.get_cur_editor() {
|
|
||||||
node.send_cmd_obj(cmd_obj)
|
|
||||||
} else {
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Diagnostics for ProductEditor {
|
|
||||||
fn get_msg_port(&self) -> OuterViewPort<dyn SequenceView<Item = crate::diagnostics::Message>> {
|
|
||||||
self.msg_port.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
|
|
||||||
pub mod editor;
|
|
||||||
pub mod segment;
|
|
||||||
pub mod nav;
|
|
||||||
|
|
||||||
pub use editor::ProductEditor;
|
|
||||||
|
|
|
@ -1,247 +0,0 @@
|
||||||
use {
|
|
||||||
crate::{
|
|
||||||
type_system::Context,
|
|
||||||
tree::{TreeNav, TreeNavResult, TreeCursor},
|
|
||||||
editors::{
|
|
||||||
list::ListCursorMode,
|
|
||||||
product::{segment::ProductEditorSegment, ProductEditor},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
cgmath::{Vector2},
|
|
||||||
std::{ops::{DerefMut}},
|
|
||||||
};
|
|
||||||
|
|
||||||
impl TreeNav for ProductEditor {
|
|
||||||
fn get_cursor(&self) -> TreeCursor {
|
|
||||||
if let Some(i) = self.cursor {
|
|
||||||
if let Some(e) = self.get_editor(i) {
|
|
||||||
let mut c = e.get_cursor();
|
|
||||||
if c.tree_addr.len() == 0 {
|
|
||||||
c.leaf_mode = ListCursorMode::Select;
|
|
||||||
}
|
|
||||||
c.tree_addr.insert(0, i);
|
|
||||||
c
|
|
||||||
} else {
|
|
||||||
TreeCursor {
|
|
||||||
leaf_mode: ListCursorMode::Select,
|
|
||||||
tree_addr: vec![ i ]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TreeCursor::none()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_cursor_warp(&self) -> TreeCursor {
|
|
||||||
if let Some(i) = self.cursor {
|
|
||||||
if let Some(e) = self.get_editor(i) {
|
|
||||||
let mut c = e.get_cursor_warp();
|
|
||||||
if c.tree_addr.len() == 0 {
|
|
||||||
c.leaf_mode = ListCursorMode::Select;
|
|
||||||
}
|
|
||||||
c.tree_addr.insert(0, i as isize - self.n_indices.len() as isize);
|
|
||||||
c
|
|
||||||
} else {
|
|
||||||
TreeCursor {
|
|
||||||
leaf_mode: ListCursorMode::Select,
|
|
||||||
tree_addr: vec![ i as isize - self.n_indices.len() as isize ]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TreeCursor::none()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn goto(&mut self, mut c: TreeCursor) -> TreeNavResult {
|
|
||||||
let old_cursor = self.cursor;
|
|
||||||
|
|
||||||
if let Some(mut segment) = self.get_cur_segment_mut() {
|
|
||||||
if let Some(ProductEditorSegment::N{ t: _t, editor, ed_depth: _, cur_depth: _, cur_dist:_ }) = segment.deref_mut() {
|
|
||||||
if let Some(e) = editor {
|
|
||||||
e.goto(TreeCursor::none());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.tree_addr.len() > 0 {
|
|
||||||
self.cursor = Some(crate::utils::modulo(c.tree_addr.remove(0), self.n_indices.len() as isize));
|
|
||||||
if let Some(mut element) = self.get_cur_segment_mut() {
|
|
||||||
if let Some(ProductEditorSegment::N{ t, editor, ed_depth, cur_depth: _, cur_dist:_ }) = element.deref_mut() {
|
|
||||||
if let Some(e) = editor {
|
|
||||||
e.goto(c.clone());
|
|
||||||
} else if c.tree_addr.len() > 0 {
|
|
||||||
// create editor
|
|
||||||
let mut e = Context::make_node(&self.ctx, t.clone(), r3vi::buffer::singleton::SingletonBuffer::new(*ed_depth+1).get_port()).unwrap();
|
|
||||||
*editor = Some(e.clone());
|
|
||||||
e.goto(c.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(i) = old_cursor{
|
|
||||||
self.update_segment(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(i) = self.cursor {
|
|
||||||
self.update_segment(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
TreeNavResult::Continue
|
|
||||||
} else {
|
|
||||||
if let Some(mut ed) = self.get_cur_editor() {
|
|
||||||
ed.goto(TreeCursor::none());
|
|
||||||
}
|
|
||||||
|
|
||||||
self.cursor = None;
|
|
||||||
|
|
||||||
if let Some(i) = old_cursor {
|
|
||||||
self.update_segment(i);
|
|
||||||
}
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn goby(&mut self, direction: Vector2<isize>) -> TreeNavResult {
|
|
||||||
let mut cur = self.get_cursor();
|
|
||||||
|
|
||||||
match cur.tree_addr.len() {
|
|
||||||
0 => {
|
|
||||||
if direction.y > 0 {
|
|
||||||
self.cursor = Some(0);
|
|
||||||
self.update_segment(0);
|
|
||||||
|
|
||||||
self.goby(Vector2::new(direction.x, direction.y-1));
|
|
||||||
TreeNavResult::Continue
|
|
||||||
} else if direction.y < 0 {
|
|
||||||
TreeNavResult::Exit
|
|
||||||
} else {
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1 => {
|
|
||||||
if direction.y > 0 {
|
|
||||||
// dn
|
|
||||||
if let Some(mut element) = self.get_cur_segment_mut() {
|
|
||||||
if let Some(ProductEditorSegment::N{ t, editor, ed_depth, cur_depth: _, cur_dist:_ }) = element.deref_mut() {
|
|
||||||
if let Some(e) = editor {
|
|
||||||
e.goby(direction);
|
|
||||||
} else {
|
|
||||||
// create editor
|
|
||||||
|
|
||||||
let mut e = Context::make_node(&self.ctx, t.clone(), r3vi::buffer::singleton::SingletonBuffer::new(*ed_depth+1).get_port()).unwrap();
|
|
||||||
*editor = Some(e.clone());
|
|
||||||
e.goby(direction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.update_segment(cur.tree_addr[0]);
|
|
||||||
TreeNavResult::Continue
|
|
||||||
} else if direction.y < 0 {
|
|
||||||
// up
|
|
||||||
let old_cursor = self.cursor;
|
|
||||||
self.cursor = None;
|
|
||||||
if let Some(i) = old_cursor {
|
|
||||||
self.update_segment(i);
|
|
||||||
}
|
|
||||||
TreeNavResult::Exit
|
|
||||||
} else {
|
|
||||||
let old_cursor = self.cursor;
|
|
||||||
|
|
||||||
if (cur.tree_addr[0]+direction.x >= 0) &&
|
|
||||||
(cur.tree_addr[0]+direction.x < self.n_indices.len() as isize)
|
|
||||||
{
|
|
||||||
self.cursor = Some(cur.tree_addr[0] + direction.x);
|
|
||||||
|
|
||||||
self.update_cur_segment();
|
|
||||||
if let Some(i) = old_cursor {
|
|
||||||
self.update_segment(i);
|
|
||||||
}
|
|
||||||
TreeNavResult::Continue
|
|
||||||
} else {
|
|
||||||
self.cursor = None;
|
|
||||||
if let Some(i) = old_cursor {
|
|
||||||
self.update_segment(i);
|
|
||||||
}
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
depth => {
|
|
||||||
let old_cursor = self.cursor;
|
|
||||||
let nav_result =
|
|
||||||
if let Some(mut element) = self.get_cur_segment_mut() {
|
|
||||||
if let Some(ProductEditorSegment::N{ t: _, editor, ed_depth: _, cur_depth, cur_dist:_ }) = element.deref_mut() {
|
|
||||||
if let Some(e) = editor.as_mut() {
|
|
||||||
//\\//\\//\\//\\
|
|
||||||
// horizontal //
|
|
||||||
//\\//\\//\\//\\
|
|
||||||
match e.goby(direction) {
|
|
||||||
TreeNavResult::Exit => {
|
|
||||||
// *cur_depth = 1;
|
|
||||||
|
|
||||||
if direction.y < 0 {
|
|
||||||
if depth <= (1-direction.y) as usize {
|
|
||||||
// up
|
|
||||||
TreeNavResult::Continue
|
|
||||||
} else {
|
|
||||||
panic!("unplausible direction.y on exit");
|
|
||||||
}
|
|
||||||
} else if direction.y > 0 {
|
|
||||||
// dn
|
|
||||||
TreeNavResult::Continue
|
|
||||||
} else if direction.y == 0 {
|
|
||||||
// horizontal
|
|
||||||
if direction.x != 0 {
|
|
||||||
*cur_depth = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cur.tree_addr[0]+direction.x >= 0) &&
|
|
||||||
(cur.tree_addr[0]+direction.x < self.n_indices.len() as isize)
|
|
||||||
{
|
|
||||||
if direction.x < 0 {
|
|
||||||
cur.tree_addr[0] -= 1;
|
|
||||||
for i in 1..depth {
|
|
||||||
cur.tree_addr[i] = -1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cur.tree_addr[0] += 1;
|
|
||||||
for i in 1..depth {
|
|
||||||
cur.tree_addr[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.goto(cur)
|
|
||||||
} else {
|
|
||||||
self.cursor = None;
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TreeNavResult::Continue => {
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TreeNavResult::Continue
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(i) = old_cursor {
|
|
||||||
self.update_segment(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.update_cur_segment();
|
|
||||||
return nav_result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,83 +0,0 @@
|
||||||
use {
|
|
||||||
r3vi::{
|
|
||||||
view::{
|
|
||||||
OuterViewPort
|
|
||||||
}
|
|
||||||
},
|
|
||||||
laddertypes::{TypeTerm},
|
|
||||||
crate::{
|
|
||||||
type_system::{Context},
|
|
||||||
terminal::{
|
|
||||||
TerminalStyle, TerminalView,
|
|
||||||
make_label
|
|
||||||
},
|
|
||||||
utils::color::{bg_style_from_depth, fg_style_from_depth},
|
|
||||||
tree::{NestedNode, TreeNav}
|
|
||||||
},
|
|
||||||
std::{sync::{Arc, RwLock}},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum ProductEditorSegment {
|
|
||||||
T( String, usize ),
|
|
||||||
N {
|
|
||||||
t: TypeTerm,
|
|
||||||
editor: Option<NestedNode>,
|
|
||||||
ed_depth: usize,
|
|
||||||
cur_depth: usize,
|
|
||||||
cur_dist: isize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ProductEditorSegment {
|
|
||||||
pub fn get_view(&self, ctx: Arc<RwLock<Context>>) -> OuterViewPort<dyn TerminalView> {
|
|
||||||
match self {
|
|
||||||
ProductEditorSegment::T(t, depth) =>
|
|
||||||
make_label(t.as_str())
|
|
||||||
.map_item({
|
|
||||||
let depth = *depth;
|
|
||||||
move |_i, x|
|
|
||||||
x.add_style_back(fg_style_from_depth(depth)).add_style_back(TerminalStyle::italic(true))
|
|
||||||
}
|
|
||||||
),
|
|
||||||
|
|
||||||
ProductEditorSegment::N { t: _, editor: Some(e), ed_depth, cur_depth: _, cur_dist } =>
|
|
||||||
e.get_view()
|
|
||||||
.map_item({
|
|
||||||
let e = e.clone();
|
|
||||||
let d = *ed_depth;
|
|
||||||
let cur_dist = *cur_dist;
|
|
||||||
|
|
||||||
move |_i, x| {
|
|
||||||
let c = e.get_cursor();
|
|
||||||
let cur_depth = c.tree_addr.len();
|
|
||||||
let select =
|
|
||||||
if cur_dist == 0 {
|
|
||||||
cur_depth
|
|
||||||
} else {
|
|
||||||
usize::MAX
|
|
||||||
};
|
|
||||||
|
|
||||||
return x
|
|
||||||
.add_style_back(bg_style_from_depth(select))
|
|
||||||
.add_style_back(TerminalStyle::bold(select==1))
|
|
||||||
.add_style_back(fg_style_from_depth(d));
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
ProductEditorSegment::N{ t, editor: None, ed_depth, cur_depth, cur_dist } =>
|
|
||||||
make_label(&ctx.read().unwrap().type_term_to_str(t))
|
|
||||||
.map_item({
|
|
||||||
let _cur_depth = *cur_depth;
|
|
||||||
let _ed_depth = *ed_depth;
|
|
||||||
let cur_dist = *cur_dist;
|
|
||||||
|
|
||||||
move |_i, x|
|
|
||||||
x.add_style_back(TerminalStyle::fg_color((215,140,95)))
|
|
||||||
.add_style_back(bg_style_from_depth(if cur_dist == 0 { 0 } else { usize::MAX }))
|
|
||||||
.add_style_back(TerminalStyle::bold(cur_dist == 0))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,142 +0,0 @@
|
||||||
use {
|
|
||||||
r3vi::{
|
|
||||||
view::{
|
|
||||||
ViewPort, OuterViewPort,
|
|
||||||
singleton::*,
|
|
||||||
sequence::*,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
laddertypes::{TypeTerm},
|
|
||||||
crate::{
|
|
||||||
terminal::TerminalView,
|
|
||||||
editors::list::ListCursorMode,
|
|
||||||
type_system::{Context, ReprTree},
|
|
||||||
tree::{TreeNav, TreeCursor, TreeNavResult},
|
|
||||||
diagnostics::{Diagnostics, Message},
|
|
||||||
tree::NestedNode,
|
|
||||||
commander::{ObjCommander},
|
|
||||||
PtySegment
|
|
||||||
},
|
|
||||||
cgmath::{Vector2},
|
|
||||||
std::sync::{Arc, RwLock}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct SumEditor {
|
|
||||||
cur: usize,
|
|
||||||
pub editors: Vec< NestedNode >,
|
|
||||||
|
|
||||||
addr_port: ViewPort< dyn SequenceView<Item = isize> >,
|
|
||||||
mode_port: ViewPort< dyn SingletonView<Item = ListCursorMode> >,
|
|
||||||
|
|
||||||
port: ViewPort< dyn TerminalView >,
|
|
||||||
diag_port: ViewPort< dyn SequenceView<Item = Message> >
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SumEditor {
|
|
||||||
pub fn new(
|
|
||||||
editors: Vec< NestedNode >
|
|
||||||
) -> Self {
|
|
||||||
let port = ViewPort::new();
|
|
||||||
|
|
||||||
SumEditor {
|
|
||||||
cur: 0,
|
|
||||||
editors,
|
|
||||||
port,
|
|
||||||
diag_port: ViewPort::new(),
|
|
||||||
|
|
||||||
|
|
||||||
addr_port: ViewPort::new(),
|
|
||||||
mode_port: ViewPort::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init_ctx(ctx: &Arc<RwLock<Context>>) {
|
|
||||||
ctx.write().unwrap().add_typename("Sum".into());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_node(self, ctx: Arc<RwLock<Context>>) -> NestedNode {
|
|
||||||
let view = self.pty_view();
|
|
||||||
let editor = Arc::new(RwLock::new(self));
|
|
||||||
|
|
||||||
NestedNode::new(
|
|
||||||
ctx.clone(),
|
|
||||||
ReprTree::new_arc(TypeTerm::TypeID(ctx.read().unwrap().get_typeid("Sum").unwrap())),
|
|
||||||
r3vi::buffer::singleton::SingletonBuffer::new(0).get_port()
|
|
||||||
)
|
|
||||||
.set_view(view)
|
|
||||||
.set_editor(editor.clone())
|
|
||||||
.set_cmd(editor.clone())
|
|
||||||
.set_nav(editor.clone())
|
|
||||||
// .set_diag(editor.read().unwrap().diag.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&self) -> NestedNode {
|
|
||||||
self.editors[ self.cur ].clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn select(&mut self, idx: usize) {
|
|
||||||
self.cur = idx;
|
|
||||||
|
|
||||||
let tv = self.editors[ self.cur ].get_view();
|
|
||||||
tv.add_observer( self.port.get_cast() );
|
|
||||||
self.port.update_hooks.write().unwrap().clear();
|
|
||||||
self.port.add_update_hook( Arc::new(tv.0.clone()) );
|
|
||||||
self.port.set_view( Some(tv.get_view_arc()) );
|
|
||||||
|
|
||||||
let dv = self.editors[ self.cur ].get_msg_port();
|
|
||||||
dv.add_observer( self.diag_port.get_cast() );
|
|
||||||
self.diag_port.update_hooks.write().unwrap().clear();
|
|
||||||
self.diag_port.add_update_hook( Arc::new(dv.0.clone()) );
|
|
||||||
self.diag_port.set_view( Some(dv.get_view_arc()) );
|
|
||||||
|
|
||||||
let dv = self.editors[ self.cur ].get_addr_view();
|
|
||||||
dv.add_observer( self.addr_port.get_cast() );
|
|
||||||
self.addr_port.update_hooks.write().unwrap().clear();
|
|
||||||
self.addr_port.add_update_hook( Arc::new(dv.0.clone()) );
|
|
||||||
self.addr_port.set_view( Some(dv.get_view_arc()) );
|
|
||||||
|
|
||||||
let dv = self.editors[ self.cur ].get_mode_view();
|
|
||||||
dv.add_observer( self.mode_port.get_cast() );
|
|
||||||
self.mode_port.update_hooks.write().unwrap().clear();
|
|
||||||
self.mode_port.add_update_hook( Arc::new(dv.0.clone()) );
|
|
||||||
self.mode_port.set_view( Some(dv.get_view_arc()) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TreeNav for SumEditor {
|
|
||||||
fn get_cursor(&self) -> TreeCursor {
|
|
||||||
self.editors[ self.cur ].get_cursor()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_cursor_warp(&self) -> TreeCursor {
|
|
||||||
self.editors[ self.cur ].get_cursor_warp()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn goby(&mut self, direction: Vector2<isize>) -> TreeNavResult {
|
|
||||||
self.editors[ self.cur ].goby( direction )
|
|
||||||
}
|
|
||||||
|
|
||||||
fn goto(&mut self, new_cursor: TreeCursor) -> TreeNavResult {
|
|
||||||
self.editors[ self.cur ].goto( new_cursor )
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_addr_view(&self) -> OuterViewPort<dyn SequenceView<Item = isize>> {
|
|
||||||
self.addr_port.outer()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_mode_view(&self) -> OuterViewPort<dyn SingletonView<Item = ListCursorMode>> {
|
|
||||||
self.mode_port.outer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PtySegment for SumEditor {
|
|
||||||
fn pty_view(&self) -> OuterViewPort<dyn TerminalView> {
|
|
||||||
self.port.outer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ObjCommander for SumEditor {
|
|
||||||
fn send_cmd_obj(&mut self, obj: Arc<RwLock<ReprTree>>) -> TreeNavResult {
|
|
||||||
self.editors[ self.cur ].send_cmd_obj( obj )
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
|
|
||||||
pub mod editor;
|
|
||||||
pub use editor::SumEditor;
|
|
||||||
|
|
|
@ -1,277 +0,0 @@
|
||||||
use {
|
|
||||||
r3vi::{
|
|
||||||
view::{singleton::*}
|
|
||||||
},
|
|
||||||
crate::{
|
|
||||||
type_system::{Context, ReprTree},
|
|
||||||
editors::{list::{ListEditor, ListCmd, ListCursorMode}},
|
|
||||||
tree::{NestedNode, TreeNav, TreeNavResult, TreeCursor},
|
|
||||||
commander::ObjCommander
|
|
||||||
},
|
|
||||||
std::{sync::{Arc, RwLock}},
|
|
||||||
|
|
||||||
super::{TypeTermEditor, State}
|
|
||||||
};
|
|
||||||
|
|
||||||
impl ObjCommander for TypeTermEditor {
|
|
||||||
fn send_cmd_obj(&mut self, co: Arc<RwLock<ReprTree>>) -> TreeNavResult {
|
|
||||||
let _cur = self.get_cursor();
|
|
||||||
|
|
||||||
let cmd_obj = co.clone();
|
|
||||||
let cmd_obj = cmd_obj.read().unwrap();
|
|
||||||
|
|
||||||
if cmd_obj.get_type().clone() == Context::parse(&self.ctx, "Char") {
|
|
||||||
if let Some(cmd_view) = cmd_obj.get_view::<dyn SingletonView<Item = char>>() {
|
|
||||||
let c = cmd_view.get();
|
|
||||||
|
|
||||||
match &self.state {
|
|
||||||
State::Any => {
|
|
||||||
match c {
|
|
||||||
'<' => {
|
|
||||||
self.set_state( State::App );
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
'0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9' => {
|
|
||||||
self.set_state( State::Num );
|
|
||||||
self.send_child_cmd( co );
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
'\'' => {
|
|
||||||
self.set_state( State::Char );
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
'~' => {
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
self.set_state( State::FunSymbol );
|
|
||||||
self.cur_node.get_mut().goto(TreeCursor::home());
|
|
||||||
self.send_child_cmd( co )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
State::Char => {
|
|
||||||
match self.send_child_cmd( co ) {
|
|
||||||
TreeNavResult::Exit => {
|
|
||||||
match c {
|
|
||||||
'\'' => {
|
|
||||||
self.cur_node.get_mut().goto(TreeCursor::none());
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
TreeNavResult::Exit
|
|
||||||
},
|
|
||||||
TreeNavResult::Continue => TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
State::Ladder => {
|
|
||||||
|
|
||||||
match self.get_cursor().tree_addr.len() {
|
|
||||||
|
|
||||||
// entire term is selected
|
|
||||||
0 => {
|
|
||||||
match c {
|
|
||||||
'<' => {
|
|
||||||
self.morph_to_list(State::App);
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
_ => { TreeNavResult::Exit }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// entire item selected or insert mode
|
|
||||||
1 => {
|
|
||||||
match c {
|
|
||||||
'~' => {
|
|
||||||
// ignore '~' since we are already in a ladder
|
|
||||||
// and cant split current item
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
self.send_child_cmd( co.clone() )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// some subterm
|
|
||||||
_ => {
|
|
||||||
match c {
|
|
||||||
'~' => {
|
|
||||||
let i0 = self.cur_node.get().get_edit::<ListEditor>().unwrap();
|
|
||||||
let cur_it = i0.clone().read().unwrap().get_item().clone();
|
|
||||||
if let Some(i) = cur_it {
|
|
||||||
let cur_tte = i.get_edit::<TypeTermEditor>().unwrap();
|
|
||||||
if cur_tte.read().unwrap().state == State::App || cur_tte.read().unwrap().get_cursor().tree_addr.len() > 1 {
|
|
||||||
self.send_child_cmd( co.clone() )
|
|
||||||
} else {
|
|
||||||
drop(cur_tte);
|
|
||||||
drop(i);
|
|
||||||
|
|
||||||
self.send_child_cmd(
|
|
||||||
ListCmd::Split.into_repr_tree( &self.ctx )
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.send_child_cmd( co.clone() )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
self.send_child_cmd( co.clone() )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
State::App => {
|
|
||||||
|
|
||||||
match self.get_cursor().tree_addr.len() {
|
|
||||||
|
|
||||||
// entire Term is selected
|
|
||||||
0 => {
|
|
||||||
|
|
||||||
match c {
|
|
||||||
'~' => {
|
|
||||||
self.morph_to_list(State::Ladder);
|
|
||||||
self.goto(TreeCursor {
|
|
||||||
tree_addr: vec![ -1 ],
|
|
||||||
leaf_mode: ListCursorMode::Insert
|
|
||||||
});
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
'<' => {
|
|
||||||
self.morph_to_list(State::App);
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
// some item is selected
|
|
||||||
_ => {
|
|
||||||
match self.send_child_cmd( co.clone() ) {
|
|
||||||
TreeNavResult::Exit => {
|
|
||||||
match c {
|
|
||||||
'~' => {
|
|
||||||
self.previous_item_into_ladder();
|
|
||||||
TreeNavResult::Continue
|
|
||||||
},
|
|
||||||
_ => {TreeNavResult::Exit}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
TreeNavResult::Continue => {
|
|
||||||
match c {
|
|
||||||
'>'|
|
|
||||||
' ' => {
|
|
||||||
let i = self.cur_node.get().get_edit::<ListEditor>().unwrap();
|
|
||||||
let i = i.read().unwrap();
|
|
||||||
if let Some(i) = i.get_item() {
|
|
||||||
let tte = i.get_edit::<TypeTermEditor>().unwrap();
|
|
||||||
let mut tte = tte.write().unwrap();
|
|
||||||
|
|
||||||
if tte.state == State::Ladder {
|
|
||||||
tte.normalize_singleton();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
State::AnySymbol |
|
|
||||||
State::FunSymbol |
|
|
||||||
State::VarSymbol => {
|
|
||||||
let res = self.send_child_cmd( co );
|
|
||||||
match res {
|
|
||||||
TreeNavResult::Exit => {
|
|
||||||
match c {
|
|
||||||
'<' => {
|
|
||||||
self.goto(TreeCursor::none());
|
|
||||||
self.morph_to_list(State::App);
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
'~' => {
|
|
||||||
self.morph_to_list(State::Ladder);
|
|
||||||
self.set_addr(0);
|
|
||||||
self.dn();
|
|
||||||
self.send_cmd_obj(
|
|
||||||
ListCmd::Split.into_repr_tree( &self.ctx )
|
|
||||||
);
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TreeNavResult::Continue => {
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => {
|
|
||||||
self.send_child_cmd( co )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match &self.state {
|
|
||||||
State::Any => {
|
|
||||||
let cmd_repr = co.read().unwrap();
|
|
||||||
if cmd_repr.get_type().clone() == Context::parse(&self.ctx, "NestedNode") {
|
|
||||||
if let Some(view) = cmd_repr.get_view::<dyn SingletonView<Item = NestedNode>>() {
|
|
||||||
let node = view.get();
|
|
||||||
|
|
||||||
if node.data.read().unwrap().get_type().clone() == Context::parse(&self.ctx, "Char") {
|
|
||||||
self.set_state( State::AnySymbol );
|
|
||||||
} else {
|
|
||||||
self.set_state( State::Ladder );
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
eprintln!("ERROR");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.set_state( State::AnySymbol );
|
|
||||||
}
|
|
||||||
|
|
||||||
self.cur_node.get_mut().goto(TreeCursor::home());
|
|
||||||
}
|
|
||||||
State::Ladder | State::App => {
|
|
||||||
// todo: if backspace cmd and empty list, reset to Any
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let res = self.send_child_cmd( co.clone() );
|
|
||||||
|
|
||||||
self.normalize_empty();
|
|
||||||
if let Some(cmd) = co.read().unwrap().get_view::<dyn SingletonView<Item = ListCmd>>() {
|
|
||||||
match cmd.get() {
|
|
||||||
ListCmd::Split => {
|
|
||||||
if self.state == State::Ladder {
|
|
||||||
self.normalize_singleton();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ =>{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,144 +0,0 @@
|
||||||
use {
|
|
||||||
r3vi::{
|
|
||||||
view::{OuterViewPort, singleton::*}
|
|
||||||
},
|
|
||||||
laddertypes::{TypeTerm},
|
|
||||||
crate::{
|
|
||||||
type_system::{Context, MorphismTypePattern},
|
|
||||||
terminal::{TerminalStyle, TerminalProjections},
|
|
||||||
editors::{
|
|
||||||
list::{PTYListStyle, PTYListController, ListEditor, ListSegmentSequence},
|
|
||||||
typeterm::{State, TypeTermEditor}
|
|
||||||
},
|
|
||||||
PtySegment
|
|
||||||
},
|
|
||||||
std::{sync::{Arc, RwLock}},
|
|
||||||
cgmath::{Point2}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn init_ctx(ctx: &mut Context) {
|
|
||||||
ctx.add_list_typename("Type".into()); // = Lit | Sym | App | Ladder
|
|
||||||
ctx.add_list_typename("Type::Lit".into()); // = Num | char
|
|
||||||
ctx.add_list_typename("Type::Lit::Num".into()); // [0-9]*
|
|
||||||
ctx.add_list_typename("Type::Lit::Char".into()); // .
|
|
||||||
ctx.add_list_typename("Type::Sym".into()); // = Fun | Var
|
|
||||||
ctx.add_list_typename("Type::Sym::Fun".into()); // [a-zA-Z][a-zA-Z0-9]*
|
|
||||||
ctx.add_list_typename("Type::Sym::Var".into()); // [a-zA-Z][a-zA-Z0-9]*
|
|
||||||
ctx.add_list_typename("Type::App".into()); // = <T1 T2 ...>
|
|
||||||
ctx.add_list_typename("Type::Ladder".into()); // = T1~T2~...
|
|
||||||
|
|
||||||
ctx.add_morphism(
|
|
||||||
MorphismTypePattern { src_tyid: ctx.get_typeid("List"), dst_tyid: ctx.get_typeid("Type").unwrap() },
|
|
||||||
Arc::new(move |node, _dst_type:_| {
|
|
||||||
let ctx : Arc<RwLock<Context>> = Arc::new(RwLock::new(Context::with_parent(Some(node.ctx.clone()))));
|
|
||||||
ctx.write().unwrap().meta_chars.push('~');
|
|
||||||
|
|
||||||
let new_node = TypeTermEditor::with_node( ctx, node.clone(), State::Any );
|
|
||||||
Some(new_node)
|
|
||||||
}));
|
|
||||||
|
|
||||||
ctx.add_morphism(
|
|
||||||
MorphismTypePattern { src_tyid: ctx.get_typeid("List"), dst_tyid: ctx.get_typeid("Type::Ladder").unwrap() },
|
|
||||||
Arc::new(|mut node, _dst_type: _| {
|
|
||||||
PTYListController::for_node( &mut node, Some('~'), None );
|
|
||||||
|
|
||||||
let vertical_view = true;
|
|
||||||
if vertical_view {
|
|
||||||
let editor = node.get_edit::<crate::editors::list::ListEditor>().unwrap();
|
|
||||||
let mut e = editor.write().unwrap();
|
|
||||||
let seg_view = PTYListStyle::new( ("","~","") ).get_seg_seq_view( &mut e );
|
|
||||||
|
|
||||||
node = node.set_view(
|
|
||||||
seg_view.to_grid_vertical()
|
|
||||||
.map_item(
|
|
||||||
|pt,x|
|
|
||||||
if pt.y > 0 {
|
|
||||||
r3vi::buffer::vec::VecBuffer::with_data(vec![
|
|
||||||
crate::terminal::make_label("~"),
|
|
||||||
x.clone()
|
|
||||||
])
|
|
||||||
.get_port()
|
|
||||||
.to_sequence()
|
|
||||||
.to_grid_horizontal()
|
|
||||||
.flatten()
|
|
||||||
} else {
|
|
||||||
x.clone()
|
|
||||||
}
|
|
||||||
).flatten()
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
PTYListStyle::for_node( &mut node, ("","~","") );
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(node)
|
|
||||||
}));
|
|
||||||
|
|
||||||
ctx.add_morphism(
|
|
||||||
MorphismTypePattern { src_tyid: ctx.get_typeid("List"), dst_tyid: ctx.get_typeid("Type::App").unwrap() },
|
|
||||||
Arc::new( |mut node, _dst_type: _| {
|
|
||||||
PTYListController::for_node( &mut node, Some(' '), Some('>') );
|
|
||||||
PTYListStyle::for_node( &mut node, ("<"," ",">") );
|
|
||||||
Some(node)
|
|
||||||
}));
|
|
||||||
|
|
||||||
ctx.add_morphism(
|
|
||||||
MorphismTypePattern { src_tyid: ctx.get_typeid("List"), dst_tyid: ctx.get_typeid("Type::Sym").unwrap() },
|
|
||||||
Arc::new(|mut node, _dst_type:_| {
|
|
||||||
PTYListController::for_node( &mut node, Some(' '), None );
|
|
||||||
PTYListStyle::for_node( &mut node, ("","","") );
|
|
||||||
Some(node)
|
|
||||||
}));
|
|
||||||
|
|
||||||
ctx.add_morphism(
|
|
||||||
MorphismTypePattern { src_tyid: ctx.get_typeid("List"), dst_tyid: ctx.get_typeid("Type::Sym::Fun").unwrap() },
|
|
||||||
Arc::new(|mut node, _dst_type:_| {
|
|
||||||
PTYListController::for_node( &mut node, Some(' '), None );
|
|
||||||
PTYListStyle::for_node( &mut node, ("","","") );
|
|
||||||
|
|
||||||
Some(node)
|
|
||||||
}));
|
|
||||||
|
|
||||||
ctx.add_morphism(
|
|
||||||
MorphismTypePattern { src_tyid: ctx.get_typeid("List"), dst_tyid: ctx.get_typeid("Type::Sym::Var").unwrap() },
|
|
||||||
Arc::new(|mut node, _dst_type:_| {
|
|
||||||
PTYListController::for_node( &mut node, Some(' '), None );
|
|
||||||
PTYListStyle::for_node( &mut node, ("","","") );
|
|
||||||
|
|
||||||
Some(node)
|
|
||||||
}));
|
|
||||||
|
|
||||||
ctx.add_morphism(
|
|
||||||
MorphismTypePattern { src_tyid: ctx.get_typeid("PosInt"), dst_tyid: ctx.get_typeid("Type::Lit::Num").unwrap() },
|
|
||||||
Arc::new(|node, _dst_type:_| {
|
|
||||||
Some(node)
|
|
||||||
}));
|
|
||||||
|
|
||||||
ctx.add_morphism(
|
|
||||||
MorphismTypePattern { src_tyid: ctx.get_typeid("Char"), dst_tyid: ctx.get_typeid("Type::Lit::Char").unwrap() },
|
|
||||||
Arc::new(|mut node, _dst_type:_| {
|
|
||||||
node.ctx.write().unwrap().meta_chars = vec![ '\'' ];
|
|
||||||
let mut grid = r3vi::buffer::index_hashmap::IndexBuffer::new();
|
|
||||||
|
|
||||||
grid.insert_iter(
|
|
||||||
vec![
|
|
||||||
(Point2::new(0,0), crate::terminal::make_label("'")),
|
|
||||||
(Point2::new(1,0), node.view.clone().unwrap_or( crate::terminal::make_label(".").with_fg_color((220,200,20))) ),
|
|
||||||
(Point2::new(2,0), crate::terminal::make_label("'")),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
node.close_char.set(Some('\''));
|
|
||||||
node = node.set_view(
|
|
||||||
grid.get_port()
|
|
||||||
.flatten()
|
|
||||||
);
|
|
||||||
|
|
||||||
Some(node)
|
|
||||||
}));
|
|
||||||
|
|
||||||
ctx.add_node_ctor("Type", Arc::new(
|
|
||||||
|ctx: Arc<RwLock<Context>>, _ty: TypeTerm, depth: OuterViewPort<dyn SingletonView<Item = usize>>| {
|
|
||||||
Some(TypeTermEditor::new_node(ctx, depth))
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,394 +0,0 @@
|
||||||
mod ctx;
|
|
||||||
mod nav;
|
|
||||||
mod cmd;
|
|
||||||
|
|
||||||
pub use ctx::init_ctx;
|
|
||||||
|
|
||||||
use {
|
|
||||||
r3vi::{
|
|
||||||
view::{OuterViewPort, singleton::*},
|
|
||||||
buffer::{singleton::*}
|
|
||||||
},
|
|
||||||
laddertypes::{TypeID, TypeTerm},
|
|
||||||
crate::{
|
|
||||||
type_system::{Context, ReprTree},
|
|
||||||
editors::{list::{ListCursorMode, ListEditor, ListCmd}},
|
|
||||||
tree::{NestedNode, TreeNav, TreeNavResult, TreeCursor},
|
|
||||||
commander::ObjCommander
|
|
||||||
},
|
|
||||||
std::{sync::{Arc, RwLock}}
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
|
||||||
pub(super) enum State {
|
|
||||||
Any,
|
|
||||||
Num,
|
|
||||||
Char,
|
|
||||||
AnySymbol,
|
|
||||||
FunSymbol,
|
|
||||||
VarSymbol,
|
|
||||||
App,
|
|
||||||
Ladder,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct TypeTermEditor {
|
|
||||||
|
|
||||||
// shared with NestedNode that contains self
|
|
||||||
ctx: Arc<RwLock<Context>>,
|
|
||||||
data: Arc<RwLock<ReprTree>>,
|
|
||||||
close_char: SingletonBuffer<Option<char>>,
|
|
||||||
spillbuf: Arc<RwLock<Vec<Arc<RwLock<NestedNode>>>>>,
|
|
||||||
depth: OuterViewPort<dyn SingletonView<Item = usize>>,
|
|
||||||
|
|
||||||
buf: SingletonBuffer< TypeTerm >,
|
|
||||||
|
|
||||||
// editing/parsing state
|
|
||||||
state: State,
|
|
||||||
|
|
||||||
// child node
|
|
||||||
cur_node: SingletonBuffer< NestedNode >
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TypeTermEditor {
|
|
||||||
pub fn from_type_term(ctx: Arc<RwLock<Context>>, depth: OuterViewPort<dyn SingletonView<Item = usize>>, term: &TypeTerm) -> NestedNode {
|
|
||||||
let mut node = TypeTermEditor::new_node(ctx.clone(), depth.clone());
|
|
||||||
node.goto(TreeCursor::home());
|
|
||||||
|
|
||||||
match term {
|
|
||||||
TypeTerm::TypeID( tyid ) => {
|
|
||||||
let editor = node.get_edit::<TypeTermEditor>().expect("typ term edit");
|
|
||||||
editor.write().unwrap().set_state(match tyid {
|
|
||||||
TypeID::Fun(_) => State::FunSymbol,
|
|
||||||
TypeID::Var(_) => State::VarSymbol
|
|
||||||
});
|
|
||||||
|
|
||||||
let typename = ctx.read().unwrap().get_typename(&tyid).unwrap_or("UNNAMED TYPE".into());
|
|
||||||
for x in typename.chars() {
|
|
||||||
node.send_cmd_obj(
|
|
||||||
ReprTree::from_char( &ctx, x )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
TypeTerm::App( args ) => {
|
|
||||||
let editor = node.get_edit::<TypeTermEditor>().expect("typ term edit");
|
|
||||||
editor.write().unwrap().set_state( State::App );
|
|
||||||
|
|
||||||
let parent_ctx = editor.read().unwrap().cur_node.get().ctx.clone();
|
|
||||||
|
|
||||||
for x in args.iter() {
|
|
||||||
let arg_node = TypeTermEditor::from_type_term( parent_ctx.clone(), depth.map(|d| d+1), x );
|
|
||||||
|
|
||||||
node.send_cmd_obj(
|
|
||||||
ReprTree::new_leaf(
|
|
||||||
Context::parse(&ctx, "NestedNode"),
|
|
||||||
SingletonBuffer::new(arg_node).get_port().into()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeTerm::Ladder( args ) => {
|
|
||||||
let editor = node.get_edit::<TypeTermEditor>().expect("typ term edit");
|
|
||||||
editor.write().unwrap().set_state( State::Ladder );
|
|
||||||
|
|
||||||
let parent_ctx = editor.read().unwrap().cur_node.get().ctx.clone();
|
|
||||||
|
|
||||||
for x in args.iter() {
|
|
||||||
let arg_node = TypeTermEditor::from_type_term( parent_ctx.clone(), depth.map(|d| d+1), x );
|
|
||||||
|
|
||||||
node.send_cmd_obj(
|
|
||||||
ReprTree::new_leaf(
|
|
||||||
Context::parse(&ctx, "NestedNode"),
|
|
||||||
SingletonBuffer::new(arg_node).get_port().into()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeTerm::Num( n ) => {
|
|
||||||
let editor = node.get_edit::<TypeTermEditor>().expect("typ term edit");
|
|
||||||
let parent_ctx = editor.read().unwrap().cur_node.get().ctx.clone();
|
|
||||||
|
|
||||||
let mut editor = editor.write().unwrap();
|
|
||||||
editor.cur_node.set(
|
|
||||||
crate::editors::integer::PosIntEditor::from_u64(parent_ctx, 10, *n as u64)
|
|
||||||
.into_node()
|
|
||||||
);
|
|
||||||
editor.state = State::Num;
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeTerm::Char( c ) => {
|
|
||||||
let editor = node.get_edit::<TypeTermEditor>().expect("typ term edit");
|
|
||||||
let mut editor = editor.write().unwrap();
|
|
||||||
editor.set_state( State::Char );
|
|
||||||
editor.send_cmd_obj(ReprTree::from_char(&ctx, *c));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
node.goto(TreeCursor::none());
|
|
||||||
node
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_state(&mut self, new_state: State) {
|
|
||||||
let mut node = match new_state {
|
|
||||||
State::Any => {
|
|
||||||
Context::make_node( &self.ctx, Context::parse(&self.ctx, "<List Char>"), self.depth.map(|x| x) ).unwrap()
|
|
||||||
.morph( Context::parse(&self.ctx, "Type::Sym") )
|
|
||||||
}
|
|
||||||
State::App => {
|
|
||||||
Context::make_node( &self.ctx, Context::parse(&self.ctx, "<List Type>"), self.depth.map(|x| x) ).unwrap()
|
|
||||||
.morph( Context::parse(&self.ctx, "Type::App") )
|
|
||||||
}
|
|
||||||
State::Ladder => {
|
|
||||||
Context::make_node( &self.ctx, Context::parse(&self.ctx, "<List Type>"), self.depth.map(|x| x) ).unwrap()
|
|
||||||
.morph( Context::parse(&self.ctx, "Type::Ladder") )
|
|
||||||
}
|
|
||||||
State::AnySymbol => {
|
|
||||||
Context::make_node( &self.ctx, Context::parse(&self.ctx, "<List Char>"), self.depth.map(|x| x) ).unwrap()
|
|
||||||
.morph( Context::parse(&self.ctx, "Type::Sym") )
|
|
||||||
},
|
|
||||||
State::FunSymbol => {
|
|
||||||
Context::make_node( &self.ctx, Context::parse(&self.ctx, "<List Char>"), self.depth.map(|x| x) ).unwrap()
|
|
||||||
.morph( Context::parse(&self.ctx, "Type::Sym::Fun") )
|
|
||||||
},
|
|
||||||
State::VarSymbol => {
|
|
||||||
Context::make_node( &self.ctx, Context::parse(&self.ctx, "<List Char>"), self.depth.map(|x| x) ).unwrap()
|
|
||||||
.morph( Context::parse(&self.ctx, "Type::Sym::Var") )
|
|
||||||
}
|
|
||||||
State::Num => {
|
|
||||||
crate::editors::integer::PosIntEditor::new(self.ctx.clone(), 10)
|
|
||||||
.into_node()
|
|
||||||
.morph( Context::parse(&self.ctx, "Type::Lit::Num") )
|
|
||||||
}
|
|
||||||
State::Char => {
|
|
||||||
Context::make_node( &self.ctx, Context::parse(&self.ctx, "Char"), self.depth.map(|x| x) ).unwrap()
|
|
||||||
.morph( Context::parse(&self.ctx, "Type::Lit::Char") )
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
node.goto(TreeCursor::home());
|
|
||||||
|
|
||||||
let _editor = node.editor.get();
|
|
||||||
self.close_char.set(node.close_char.get());
|
|
||||||
self.cur_node.set(node);
|
|
||||||
self.state = new_state;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_node(ctx: Arc<RwLock<Context>>, depth: OuterViewPort<dyn SingletonView<Item = usize>>) -> NestedNode {
|
|
||||||
let ctx : Arc<RwLock<Context>> = Arc::new(RwLock::new(Context::with_parent(Some(ctx))));
|
|
||||||
ctx.write().unwrap().meta_chars.push('~');
|
|
||||||
ctx.write().unwrap().meta_chars.push('<');
|
|
||||||
|
|
||||||
let mut symb_node = Context::make_node( &ctx, Context::parse(&ctx, "<List Char>"), depth ).unwrap();
|
|
||||||
symb_node = symb_node.morph( Context::parse(&ctx, "Type::Sym") );
|
|
||||||
|
|
||||||
Self::with_node(
|
|
||||||
ctx.clone(),
|
|
||||||
symb_node,
|
|
||||||
State::Any
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_node(ctx: Arc<RwLock<Context>>, cur_node: NestedNode, state: State) -> NestedNode {
|
|
||||||
let buf = SingletonBuffer::<TypeTerm>::new( TypeTerm::unit() );
|
|
||||||
|
|
||||||
let data = Arc::new(RwLock::new(ReprTree::new(
|
|
||||||
Context::parse(&ctx, "Type")
|
|
||||||
)));
|
|
||||||
|
|
||||||
let editor = TypeTermEditor {
|
|
||||||
ctx: ctx.clone(),
|
|
||||||
data: data.clone(),
|
|
||||||
state,
|
|
||||||
buf,
|
|
||||||
cur_node: SingletonBuffer::new(cur_node.clone()),
|
|
||||||
close_char: SingletonBuffer::new(None),
|
|
||||||
spillbuf: Arc::new(RwLock::new(Vec::new())),
|
|
||||||
depth: cur_node.depth.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
let view = editor.cur_node
|
|
||||||
.get_port()
|
|
||||||
.map(|node| {
|
|
||||||
node.view.clone().unwrap_or(r3vi::view::ViewPort::new().into_outer())
|
|
||||||
})
|
|
||||||
.to_grid()
|
|
||||||
.flatten();
|
|
||||||
let _cc = editor.cur_node.get().close_char;
|
|
||||||
let editor = Arc::new(RwLock::new(editor));
|
|
||||||
|
|
||||||
let mut super_node = NestedNode::new(ctx, data, cur_node.depth)
|
|
||||||
.set_view(view)
|
|
||||||
.set_nav(editor.clone())
|
|
||||||
.set_cmd(editor.clone())
|
|
||||||
.set_editor(editor.clone());
|
|
||||||
|
|
||||||
editor.write().unwrap().close_char = super_node.close_char.clone();
|
|
||||||
super_node.spillbuf = editor.read().unwrap().spillbuf.clone();
|
|
||||||
|
|
||||||
super_node
|
|
||||||
}
|
|
||||||
|
|
||||||
fn forward_spill(&mut self) {
|
|
||||||
let node = self.cur_node.get();
|
|
||||||
let mut buf = node.spillbuf.write().unwrap();
|
|
||||||
for n in buf.iter() {
|
|
||||||
self.spillbuf.write().unwrap().push(n.clone());
|
|
||||||
}
|
|
||||||
buf.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_child_cmd(&mut self, cmd: Arc<RwLock<ReprTree>>) -> TreeNavResult {
|
|
||||||
let res = self.cur_node.get_mut().send_cmd_obj( cmd );
|
|
||||||
self.forward_spill();
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_typeterm(&self) -> Option<TypeTerm> {
|
|
||||||
match self.state {
|
|
||||||
State::Any => None,
|
|
||||||
|
|
||||||
State::AnySymbol => {
|
|
||||||
/*
|
|
||||||
let x = self.data.descend_ladder(vec![
|
|
||||||
(&ctx, "( FunctionID )").into(),
|
|
||||||
(&ctx, "( Symbol )").into(),
|
|
||||||
(&ctx, "( List Char )").into(),
|
|
||||||
].into_iter());
|
|
||||||
|
|
||||||
let fun_name = /* x...*/ "PosInt";
|
|
||||||
let fun_id = self.ctx.read().unwrap().get_typeid( fun_name );
|
|
||||||
|
|
||||||
self.data.add_repr(
|
|
||||||
vec![
|
|
||||||
(&ctx, "( FunctionID )").into(),
|
|
||||||
(&ctx, "( MachineInt )").into()
|
|
||||||
]
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
Some(TypeTerm::new(TypeID::Fun(0)))
|
|
||||||
},
|
|
||||||
|
|
||||||
State::App => {
|
|
||||||
Some(TypeTerm::new(TypeID::Fun(0)))
|
|
||||||
},
|
|
||||||
|
|
||||||
State::Char => {
|
|
||||||
Some(TypeTerm::Char('c'))
|
|
||||||
}
|
|
||||||
State::Num => {
|
|
||||||
Some(TypeTerm::Num(44))
|
|
||||||
}
|
|
||||||
_ => {None}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn normalize_empty(&mut self) {
|
|
||||||
eprintln!("normalize singleton");
|
|
||||||
let subladder_list_node = self.cur_node.get().clone();
|
|
||||||
let subladder_list_edit = subladder_list_node.get_edit::<ListEditor>().unwrap();
|
|
||||||
|
|
||||||
let subladder_list_edit = subladder_list_edit.read().unwrap();
|
|
||||||
if subladder_list_edit.data.len() == 0 {
|
|
||||||
self.set_state( State::Any );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* unwrap a ladder if it only contains one element
|
|
||||||
*/
|
|
||||||
pub fn normalize_singleton(&mut self) {
|
|
||||||
eprintln!("normalize singleton");
|
|
||||||
|
|
||||||
if self.state == State::Ladder {
|
|
||||||
let subladder_list_node = self.cur_node.get().clone();
|
|
||||||
let subladder_list_edit = subladder_list_node.get_edit::<ListEditor>().unwrap();
|
|
||||||
|
|
||||||
let subladder_list_edit = subladder_list_edit.read().unwrap();
|
|
||||||
if subladder_list_edit.data.len() == 1 {
|
|
||||||
let it_node = subladder_list_edit.data.get(0);
|
|
||||||
let it_node = it_node.read().unwrap();
|
|
||||||
if it_node.get_type() == Context::parse(&self.ctx, "Type") {
|
|
||||||
let other_tt = it_node.get_edit::<TypeTermEditor>().unwrap();
|
|
||||||
|
|
||||||
let mut other_tt = other_tt.write().unwrap();
|
|
||||||
|
|
||||||
other_tt.normalize_singleton();
|
|
||||||
other_tt.depth.0.set_view( self.depth.map(|x| x).get_view() );
|
|
||||||
|
|
||||||
self.close_char.set(other_tt.close_char.get());
|
|
||||||
self.cur_node.set(other_tt.cur_node.get());
|
|
||||||
self.state = other_tt.state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* in insert mode, morph the previous element into a ladder and continue there
|
|
||||||
*/
|
|
||||||
pub fn previous_item_into_ladder(&mut self) {
|
|
||||||
let app_edit = self.cur_node.get().get_edit::<ListEditor>().expect("editor");
|
|
||||||
let mut app_edit = app_edit.write().unwrap();
|
|
||||||
|
|
||||||
let cur = app_edit.get_cursor();
|
|
||||||
|
|
||||||
if cur.tree_addr.len() <= 2 && cur.tree_addr.len() > 0 {
|
|
||||||
if cur.tree_addr.len() == 2 {
|
|
||||||
app_edit.delete_nexd();
|
|
||||||
}
|
|
||||||
|
|
||||||
// select previous element
|
|
||||||
app_edit.goto(TreeCursor{
|
|
||||||
tree_addr: vec![ cur.tree_addr[0]-1 ],
|
|
||||||
leaf_mode: ListCursorMode::Select
|
|
||||||
});
|
|
||||||
|
|
||||||
// get selected element
|
|
||||||
if let Some(item_node) = app_edit.get_item() {
|
|
||||||
let item_typterm = item_node.get_edit::<TypeTermEditor>().expect("typetermedit");
|
|
||||||
let mut item_typterm = item_typterm.write().unwrap();
|
|
||||||
if item_typterm.state != State::Ladder {
|
|
||||||
item_typterm.morph_to_list( State::Ladder );
|
|
||||||
}
|
|
||||||
|
|
||||||
item_typterm.goto(TreeCursor {
|
|
||||||
tree_addr: vec![ -1 ],
|
|
||||||
leaf_mode: ListCursorMode::Insert
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* replace with new list-node (ladder/app) with self as first element
|
|
||||||
*/
|
|
||||||
pub(super) fn morph_to_list(&mut self, state: State) {
|
|
||||||
eprintln!("morph into ladder");
|
|
||||||
|
|
||||||
let mut old_node = self.cur_node.get().clone();
|
|
||||||
|
|
||||||
/* reconfigure current node to display new_node list-editor
|
|
||||||
*/
|
|
||||||
self.set_state( state );
|
|
||||||
|
|
||||||
/* create a new NestedNode with TerminaltypeEditor,
|
|
||||||
* that has same state & child-node as current node.
|
|
||||||
*/
|
|
||||||
let old_edit_node = TypeTermEditor::new_node( self.ctx.clone(), SingletonBuffer::new(0).get_port() );
|
|
||||||
old_node.depth.0.set_view( old_edit_node.depth.map(|x|x).get_view() );
|
|
||||||
|
|
||||||
let old_edit_clone = old_edit_node.get_edit::<TypeTermEditor>().unwrap();
|
|
||||||
old_edit_clone.write().unwrap().set_state( self.state );
|
|
||||||
old_edit_clone.write().unwrap().cur_node.set( old_node );
|
|
||||||
|
|
||||||
/* insert old node and split
|
|
||||||
*/
|
|
||||||
self.goto(TreeCursor::home());
|
|
||||||
self.send_child_cmd(
|
|
||||||
ReprTree::new_leaf(
|
|
||||||
Context::parse(&self.ctx, "NestedNode"),
|
|
||||||
SingletonBuffer::new( old_edit_node ).get_port().into()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
use {
|
|
||||||
r3vi::{
|
|
||||||
view::{
|
|
||||||
OuterViewPort,
|
|
||||||
singleton::*,
|
|
||||||
sequence::*
|
|
||||||
}
|
|
||||||
},
|
|
||||||
crate::{
|
|
||||||
tree::{TreeNav, TreeCursor, TreeNavResult, TreeHeightOp},
|
|
||||||
editors::{typeterm::TypeTermEditor, list::ListCursorMode}
|
|
||||||
},
|
|
||||||
cgmath::Vector2
|
|
||||||
};
|
|
||||||
|
|
||||||
impl TreeNav for TypeTermEditor {
|
|
||||||
fn get_cursor(&self) -> TreeCursor {
|
|
||||||
self.cur_node.get().get_cursor()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_addr_view(&self) -> OuterViewPort<dyn SequenceView<Item = isize>> {
|
|
||||||
self.cur_node.get_port().map(|x| x.get_addr_view()).to_sequence().flatten()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_mode_view(&self) -> OuterViewPort<dyn SingletonView<Item = ListCursorMode>> {
|
|
||||||
self.cur_node.get_port().map(|x| x.get_mode_view()).flatten()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_cursor_warp(&self) -> TreeCursor {
|
|
||||||
self.cur_node.get().get_cursor_warp()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_height(&self, op: &TreeHeightOp) -> usize {
|
|
||||||
self.cur_node.get().get_height(op)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn goby(&mut self, dir: Vector2<isize>) -> TreeNavResult {
|
|
||||||
self.cur_node.get_mut().goby(dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn goto(&mut self, new_cur: TreeCursor) -> TreeNavResult {
|
|
||||||
self.cur_node.get_mut().goto(new_cur)
|
|
||||||
}
|
|
||||||
}
|
|
236
nested/src/grid/flatten.rs
Normal file
236
nested/src/grid/flatten.rs
Normal file
|
@ -0,0 +1,236 @@
|
||||||
|
use {
|
||||||
|
std::{
|
||||||
|
sync::Arc,
|
||||||
|
cmp::max,
|
||||||
|
collections::HashMap
|
||||||
|
},
|
||||||
|
std::sync::RwLock,
|
||||||
|
cgmath::{Point2, Vector2},
|
||||||
|
crate::{
|
||||||
|
core::{
|
||||||
|
View, Observer, ObserverBroadcast,
|
||||||
|
ViewPort, InnerViewPort, OuterViewPort,
|
||||||
|
port::UpdateTask
|
||||||
|
},
|
||||||
|
grid::{GridView, GridWindowIterator},
|
||||||
|
index::{IndexArea, IndexView},
|
||||||
|
projection::ProjectionHelper
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
impl<Item> OuterViewPort<dyn GridView<Item = OuterViewPort<dyn GridView<Item = Item>>>>
|
||||||
|
where Item: 'static{
|
||||||
|
pub fn flatten(&self) -> OuterViewPort<dyn GridView<Item = Item>> {
|
||||||
|
let port = ViewPort::new();
|
||||||
|
port.add_update_hook(Arc::new(self.0.clone()));
|
||||||
|
Flatten::new(self.clone(), port.inner());
|
||||||
|
port.into_outer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Chunk<Item>
|
||||||
|
where Item: 'static
|
||||||
|
{
|
||||||
|
offset: Vector2<i16>,
|
||||||
|
limit: Point2<i16>,
|
||||||
|
view: Arc<dyn GridView<Item = Item>>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Flatten<Item>
|
||||||
|
where Item: 'static
|
||||||
|
{
|
||||||
|
limit: Point2<i16>,
|
||||||
|
top: Arc<dyn GridView<Item = OuterViewPort<dyn GridView<Item = Item>>>>,
|
||||||
|
chunks: HashMap<Point2<i16>, Chunk<Item>>,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn GridView<Item = Item>>>>,
|
||||||
|
proj_helper: ProjectionHelper<Point2<i16>, Self>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Item> View for Flatten<Item>
|
||||||
|
where Item: 'static
|
||||||
|
{
|
||||||
|
type Msg = IndexArea<Point2<i16>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Item> IndexView<Point2<i16>> for Flatten<Item>
|
||||||
|
where Item: 'static
|
||||||
|
{
|
||||||
|
type Item = Item;
|
||||||
|
|
||||||
|
fn get(&self, idx: &Point2<i16>) -> Option<Self::Item> {
|
||||||
|
let chunk_idx = self.get_chunk_idx(*idx)?;
|
||||||
|
let chunk = self.chunks.get(&chunk_idx)?;
|
||||||
|
chunk.view.get(&(*idx - chunk.offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn area(&self) -> IndexArea<Point2<i16>> {
|
||||||
|
IndexArea::Range(
|
||||||
|
Point2::new(0, 0) ..= self.limit
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: remove unused projection args (bot-views) if they get replaced by a new viewport */
|
||||||
|
impl<Item> Flatten<Item>
|
||||||
|
where Item: 'static
|
||||||
|
{
|
||||||
|
pub fn new(
|
||||||
|
top_port: OuterViewPort<dyn GridView<Item = OuterViewPort<dyn GridView<Item = Item>>>>,
|
||||||
|
out_port: InnerViewPort<dyn GridView<Item = Item>>
|
||||||
|
) -> Arc<RwLock<Self>> {
|
||||||
|
let mut proj_helper = ProjectionHelper::new(out_port.0.update_hooks.clone());
|
||||||
|
|
||||||
|
let flat = Arc::new(RwLock::new(
|
||||||
|
Flatten {
|
||||||
|
limit: Point2::new(0, 0),
|
||||||
|
top: proj_helper.new_index_arg(
|
||||||
|
Point2::new(-1, -1),
|
||||||
|
top_port,
|
||||||
|
|s: &mut Self, chunk_area| {
|
||||||
|
for chunk_idx in chunk_area.iter() {
|
||||||
|
s.update_chunk(chunk_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
chunks: HashMap::new(),
|
||||||
|
cast: out_port.get_broadcast(),
|
||||||
|
proj_helper
|
||||||
|
}));
|
||||||
|
|
||||||
|
flat.write().unwrap().proj_helper.set_proj(&flat);
|
||||||
|
out_port.set_view(Some(flat.clone()));
|
||||||
|
flat
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the top-sequence has changed the item at chunk_idx,
|
||||||
|
/// create a new observer for the contained sub sequence
|
||||||
|
fn update_chunk(&mut self, chunk_idx: Point2<i16>) {
|
||||||
|
if let Some(chunk_port) = self.top.get(&chunk_idx) {
|
||||||
|
let view = self.proj_helper.new_index_arg(
|
||||||
|
chunk_idx,
|
||||||
|
chunk_port.clone(),
|
||||||
|
move |s: &mut Self, area| {
|
||||||
|
if let Some(chunk) = s.chunks.get(&chunk_idx) {
|
||||||
|
if chunk.limit != *chunk.view.area().range().end() {
|
||||||
|
s.update_all_offsets();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(chunk) = s.chunks.get(&chunk_idx) {
|
||||||
|
s.cast.notify(
|
||||||
|
&area.map(|pt| pt + chunk.offset)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(chunk) = self.chunks.get_mut(&chunk_idx) {
|
||||||
|
chunk.view = view;
|
||||||
|
self.cast.notify(
|
||||||
|
&chunk.view.area().map(|pt| pt + chunk.offset)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
self.chunks.insert(
|
||||||
|
chunk_idx,
|
||||||
|
Chunk {
|
||||||
|
offset: Vector2::new(-1, -1),
|
||||||
|
limit: Point2::new(-1, -1),
|
||||||
|
view
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.update_all_offsets();
|
||||||
|
} else {
|
||||||
|
self.proj_helper.remove_arg(&chunk_idx);
|
||||||
|
|
||||||
|
if let Some(chunk) = self.chunks.remove(&chunk_idx) {
|
||||||
|
self.update_all_offsets();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// recalculate all chunk offsets
|
||||||
|
/// and update size of flattened grid
|
||||||
|
fn update_all_offsets(&mut self) {
|
||||||
|
let top_range = self.top.area().range();
|
||||||
|
let mut col_widths = vec![0 as i16; (top_range.end().x+1) as usize];
|
||||||
|
let mut row_heights = vec![0 as i16; (top_range.end().y+1) as usize];
|
||||||
|
|
||||||
|
for chunk_idx in GridWindowIterator::from(top_range.clone()) {
|
||||||
|
if let Some(chunk) = self.chunks.get_mut(&chunk_idx) {
|
||||||
|
let chunk_range = chunk.view.area().range();
|
||||||
|
let lim = *chunk_range.end();
|
||||||
|
|
||||||
|
col_widths[chunk_idx.x as usize] =
|
||||||
|
max(
|
||||||
|
col_widths[chunk_idx.x as usize],
|
||||||
|
if lim.x < 0 { 0 } else { lim.x+1 }
|
||||||
|
);
|
||||||
|
row_heights[chunk_idx.y as usize] =
|
||||||
|
max(
|
||||||
|
row_heights[chunk_idx.y as usize],
|
||||||
|
if lim.y < 0 { 0 } else { lim.y+1 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for chunk_idx in GridWindowIterator::from(top_range.clone()) {
|
||||||
|
if let Some(chunk) = self.chunks.get_mut(&chunk_idx) {
|
||||||
|
let old_offset = chunk.offset;
|
||||||
|
let old_limit = chunk.limit;
|
||||||
|
|
||||||
|
chunk.limit = *chunk.view.area().range().end();
|
||||||
|
chunk.offset = Vector2::new(
|
||||||
|
(0 .. chunk_idx.x as usize).map(|x| col_widths[x]).sum(),
|
||||||
|
(0 .. chunk_idx.y as usize).map(|y| row_heights[y]).sum()
|
||||||
|
);
|
||||||
|
/*
|
||||||
|
if old_offset != chunk.offset {
|
||||||
|
self.cast.notify(
|
||||||
|
&IndexArea::Range(
|
||||||
|
Point2::new(
|
||||||
|
std::cmp::min(old_offset.x, chunk.offset.x),
|
||||||
|
std::cmp::min(old_offset.y, chunk.offset.y)
|
||||||
|
)
|
||||||
|
..= Point2::new(
|
||||||
|
std::cmp::max(old_offset.x + old_limit.x, chunk.offset.x + chunk.limit.x),
|
||||||
|
std::cmp::max(old_offset.y + old_limit.y, chunk.offset.y + chunk.limit.y)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let old_limit = self.limit;
|
||||||
|
self.limit = Point2::new(
|
||||||
|
(0 ..= top_range.end().x as usize).map(|x| col_widths[x]).sum::<i16>() - 1,
|
||||||
|
(0 ..= top_range.end().y as usize).map(|y| row_heights[y]).sum::<i16>() - 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.cast.notify(
|
||||||
|
&IndexArea::Range(
|
||||||
|
Point2::new(0, 0) ..= Point2::new(max(self.limit.x, old_limit.x), max(self.limit.y, old_limit.y))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// given an index in the flattened sequence,
|
||||||
|
/// which sub-sequence does it belong to?
|
||||||
|
fn get_chunk_idx(&self, glob_pos: Point2<i16>) -> Option<Point2<i16>> {
|
||||||
|
for chunk_idx in GridWindowIterator::from(self.top.area().range()) {
|
||||||
|
if let Some(chunk) = self.chunks.get(&chunk_idx) {
|
||||||
|
let end = chunk.limit + chunk.offset;
|
||||||
|
|
||||||
|
if glob_pos.x <= end.x && glob_pos.y <= end.y {
|
||||||
|
return Some(chunk_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
82
nested/src/grid/mod.rs
Normal file
82
nested/src/grid/mod.rs
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
|
||||||
|
use {
|
||||||
|
std::{
|
||||||
|
ops::RangeInclusive,
|
||||||
|
cmp::{min, max}
|
||||||
|
},
|
||||||
|
cgmath::{Point2},
|
||||||
|
crate::index::{IndexArea, IndexView}
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub trait GridView = IndexView<Point2<i16>>;
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub mod offset;
|
||||||
|
pub mod flatten;
|
||||||
|
pub mod window_iterator;
|
||||||
|
|
||||||
|
pub use window_iterator::GridWindowIterator;
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl IndexArea<Point2<i16>> {
|
||||||
|
|
||||||
|
// todo: this is not perfect (e.g. diagonals are inefficient)
|
||||||
|
pub fn iter(&self) -> GridWindowIterator {
|
||||||
|
GridWindowIterator::from(self.range())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn range(&self) -> RangeInclusive<Point2<i16>> {
|
||||||
|
match self {
|
||||||
|
IndexArea::Empty => Point2::new(i16::MAX, i16::MAX) ..= Point2::new(i16::MIN, i16::MIN),
|
||||||
|
IndexArea::Full => panic!("range from full grid area"),
|
||||||
|
IndexArea::Set(v) =>
|
||||||
|
Point2::new(
|
||||||
|
v.iter().map(|p| p.x).min().unwrap_or(0),
|
||||||
|
v.iter().map(|p| p.y).min().unwrap_or(0)
|
||||||
|
) ..=
|
||||||
|
Point2::new(
|
||||||
|
v.iter().map(|p| p.x).max().unwrap_or(0),
|
||||||
|
v.iter().map(|p| p.y).max().unwrap_or(0)
|
||||||
|
),
|
||||||
|
IndexArea::Range(r) => r.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn union(self, other: IndexArea<Point2<i16>>) -> IndexArea<Point2<i16>> {
|
||||||
|
match (self, other) {
|
||||||
|
(IndexArea::Empty, a) |
|
||||||
|
(a, IndexArea::Empty) => a,
|
||||||
|
|
||||||
|
(IndexArea::Full, _) |
|
||||||
|
(_, IndexArea::Full) => IndexArea::Full,
|
||||||
|
|
||||||
|
(IndexArea::Set(mut va), IndexArea::Set(mut vb)) => {
|
||||||
|
va.extend(vb.into_iter());
|
||||||
|
IndexArea::Set(va)
|
||||||
|
},
|
||||||
|
|
||||||
|
(IndexArea::Range(r), IndexArea::Set(mut v)) |
|
||||||
|
(IndexArea::Set(mut v), IndexArea::Range(r)) => {
|
||||||
|
v.extend(GridWindowIterator::from(r));
|
||||||
|
IndexArea::Set(v)
|
||||||
|
},
|
||||||
|
|
||||||
|
(IndexArea::Range(ra), IndexArea::Range(rb)) => IndexArea::Range(
|
||||||
|
Point2::new(
|
||||||
|
min(ra.start().x, rb.start().x),
|
||||||
|
min(ra.start().y, rb.start().y)
|
||||||
|
)
|
||||||
|
..=
|
||||||
|
Point2::new(
|
||||||
|
max(ra.end().x, rb.end().x),
|
||||||
|
max(ra.end().y, rb.end().y)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
20
nested/src/grid/offset.rs
Normal file
20
nested/src/grid/offset.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
use {
|
||||||
|
cgmath::Vector2,
|
||||||
|
crate::{
|
||||||
|
core::OuterViewPort,
|
||||||
|
grid::GridView
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<Item> OuterViewPort<dyn GridView<Item = Item>>
|
||||||
|
where Item: 'static {
|
||||||
|
pub fn offset(&self, offset: Vector2<i16>) -> OuterViewPort<dyn GridView<Item = Item>> {
|
||||||
|
self.map_key(
|
||||||
|
move |pt| pt + offset,
|
||||||
|
move |pt| Some(pt - offset)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
60
nested/src/grid/window_iterator.rs
Normal file
60
nested/src/grid/window_iterator.rs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
|
||||||
|
use {
|
||||||
|
std::ops::{Range, RangeInclusive},
|
||||||
|
cgmath::{Point2}
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub struct GridWindowIterator {
|
||||||
|
next: Point2<i16>,
|
||||||
|
range: Range<Point2<i16>>
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl From<Range<Point2<i16>>> for GridWindowIterator {
|
||||||
|
fn from(range: Range<Point2<i16>>) -> Self {
|
||||||
|
GridWindowIterator {
|
||||||
|
next: range.start,
|
||||||
|
range
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RangeInclusive<Point2<i16>>> for GridWindowIterator {
|
||||||
|
fn from(range: RangeInclusive<Point2<i16>>) -> Self {
|
||||||
|
GridWindowIterator {
|
||||||
|
next: *range.start(),
|
||||||
|
range: *range.start() .. Point2::new(range.end().x+1, range.end().y+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl Iterator for GridWindowIterator {
|
||||||
|
type Item = Point2<i16>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Point2<i16>> {
|
||||||
|
if self.next.y < self.range.end.y {
|
||||||
|
if self.next.x < self.range.end.x {
|
||||||
|
let next = self.next;
|
||||||
|
|
||||||
|
if self.next.x+1 < self.range.end.x {
|
||||||
|
self.next.x += 1;
|
||||||
|
} else {
|
||||||
|
self.next.x = self.range.start.x;
|
||||||
|
self.next.y += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(next)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
85
nested/src/index/buffer.rs
Normal file
85
nested/src/index/buffer.rs
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
|
||||||
|
use {
|
||||||
|
std::{
|
||||||
|
sync::Arc,
|
||||||
|
collections::HashMap,
|
||||||
|
hash::Hash
|
||||||
|
},
|
||||||
|
std::sync::RwLock,
|
||||||
|
crate::{
|
||||||
|
core::{
|
||||||
|
Observer,
|
||||||
|
ObserverBroadcast,
|
||||||
|
View,
|
||||||
|
InnerViewPort
|
||||||
|
},
|
||||||
|
index::{IndexArea, IndexView}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct IndexBufferView<Key, Item>(Arc<RwLock<HashMap<Key, Item>>>)
|
||||||
|
where Key: Clone + Hash + Eq + Send + Sync + 'static,
|
||||||
|
Item: Clone + Send + Sync + 'static;
|
||||||
|
|
||||||
|
impl<Key, Item> View for IndexBufferView<Key, Item>
|
||||||
|
where Key: Clone + Hash + Eq + Send + Sync + 'static,
|
||||||
|
Item: Clone + Send + Sync + 'static
|
||||||
|
{
|
||||||
|
type Msg = IndexArea<Key>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Key, Item> IndexView<Key> for IndexBufferView<Key, Item>
|
||||||
|
where Key: Clone + Hash + Eq + Send + Sync + 'static,
|
||||||
|
Item: Clone + Send + Sync + 'static
|
||||||
|
{
|
||||||
|
type Item = Item;
|
||||||
|
|
||||||
|
fn get(&self, key: &Key) -> Option<Self::Item> {
|
||||||
|
self.0.read().unwrap().get(key).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn area(&self) -> IndexArea<Key> {
|
||||||
|
IndexArea::Set(self.0.read().unwrap().keys().cloned().collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IndexBuffer<Key, Item>
|
||||||
|
where Key: Clone + Hash + Eq + Send + Sync + 'static,
|
||||||
|
Item: Clone + Send + Sync + 'static
|
||||||
|
{
|
||||||
|
data: Arc<RwLock<HashMap<Key, Item>>>,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn IndexView<Key, Item = Item>>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Key, Item> IndexBuffer<Key, Item>
|
||||||
|
where Key: Clone + Hash + Eq + Send + Sync + 'static,
|
||||||
|
Item: Clone + Send + Sync + 'static
|
||||||
|
{
|
||||||
|
pub fn new(port: InnerViewPort<dyn IndexView<Key, Item = Item>>) -> Self {
|
||||||
|
let data = Arc::new(RwLock::new(HashMap::<Key, Item>::new()));
|
||||||
|
port.set_view(Some(Arc::new(IndexBufferView(data.clone()))));
|
||||||
|
|
||||||
|
IndexBuffer {
|
||||||
|
data,
|
||||||
|
cast: port.get_broadcast()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&mut self, key: Key, item: Item) {
|
||||||
|
self.data.write().unwrap().insert(key.clone(), item);
|
||||||
|
self.cast.notify(&IndexArea::Set(vec![ key ]));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_iter<T>(&mut self, iter: T)
|
||||||
|
where T: IntoIterator<Item = (Key, Item)> {
|
||||||
|
for (key, item) in iter {
|
||||||
|
self.insert(key, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&mut self, key: Key) {
|
||||||
|
self.data.write().unwrap().remove(&key);
|
||||||
|
self.cast.notify(&IndexArea::Set(vec![ key ]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
116
nested/src/index/map_item.rs
Normal file
116
nested/src/index/map_item.rs
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
pub use {
|
||||||
|
std::{
|
||||||
|
sync::Arc,
|
||||||
|
boxed::Box
|
||||||
|
},
|
||||||
|
std::sync::RwLock,
|
||||||
|
crate::{
|
||||||
|
core::{
|
||||||
|
View,
|
||||||
|
Observer,
|
||||||
|
ObserverExt,
|
||||||
|
ObserverBroadcast,
|
||||||
|
ViewPort,
|
||||||
|
InnerViewPort,
|
||||||
|
OuterViewPort
|
||||||
|
},
|
||||||
|
index::{IndexArea, IndexView}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
impl<Key, Item> OuterViewPort<dyn IndexView<Key, Item = Item>>
|
||||||
|
where Key: Clone + Send + Sync + 'static,
|
||||||
|
Item: Send + Sync + 'static
|
||||||
|
{
|
||||||
|
pub fn map_item<
|
||||||
|
DstItem: 'static,
|
||||||
|
F: Fn(&Key, &Item) -> DstItem + Send + Sync + 'static
|
||||||
|
>(
|
||||||
|
&self,
|
||||||
|
f: F
|
||||||
|
) -> OuterViewPort<dyn IndexView<Key, Item = DstItem>> {
|
||||||
|
let port = ViewPort::new();
|
||||||
|
port.add_update_hook(Arc::new(self.0.clone()));
|
||||||
|
|
||||||
|
let map = MapIndexItem::new(port.inner(), f);
|
||||||
|
self.add_observer(map.clone());
|
||||||
|
port.into_outer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MapIndexItem<Key, DstItem, SrcView, F>
|
||||||
|
where Key: Clone + Send + Sync,
|
||||||
|
SrcView: IndexView<Key> + ?Sized,
|
||||||
|
F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync
|
||||||
|
{
|
||||||
|
src_view: Option<Arc<SrcView>>,
|
||||||
|
f: F,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn IndexView<Key, Item = DstItem>>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Key, DstItem, SrcView, F> MapIndexItem<Key, DstItem, SrcView, F>
|
||||||
|
where Key: Clone + Send + Sync + 'static,
|
||||||
|
DstItem: 'static,
|
||||||
|
SrcView: IndexView<Key> + ?Sized + 'static,
|
||||||
|
F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync + 'static
|
||||||
|
{
|
||||||
|
fn new(
|
||||||
|
port: InnerViewPort<dyn IndexView<Key, Item = DstItem>>,
|
||||||
|
f: F
|
||||||
|
) -> Arc<RwLock<Self>> {
|
||||||
|
let map = Arc::new(RwLock::new(
|
||||||
|
MapIndexItem {
|
||||||
|
src_view: None,
|
||||||
|
f,
|
||||||
|
cast: port.get_broadcast()
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
port.set_view(Some(map.clone()));
|
||||||
|
map
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Key, DstItem, SrcView, F> View for MapIndexItem<Key, DstItem, SrcView, F>
|
||||||
|
where Key: Clone + Send + Sync,
|
||||||
|
SrcView: IndexView<Key> + ?Sized,
|
||||||
|
F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync
|
||||||
|
{
|
||||||
|
type Msg = IndexArea<Key>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Key, DstItem, SrcView, F> IndexView<Key> for MapIndexItem<Key, DstItem, SrcView, F>
|
||||||
|
where Key: Clone + Send + Sync,
|
||||||
|
SrcView: IndexView<Key> + ?Sized,
|
||||||
|
F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync
|
||||||
|
{
|
||||||
|
type Item = DstItem;
|
||||||
|
|
||||||
|
fn get(&self, key: &Key) -> Option<Self::Item> {
|
||||||
|
self.src_view.get(key).as_ref().map(|item| (self.f)(key, item))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn area(&self) -> IndexArea<Key> {
|
||||||
|
self.src_view.area()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Key, DstItem, SrcView, F> Observer<SrcView> for MapIndexItem<Key, DstItem, SrcView, F>
|
||||||
|
where Key: Clone + Send + Sync,
|
||||||
|
SrcView: IndexView<Key> + ?Sized,
|
||||||
|
F: Fn(&Key, &SrcView::Item) -> DstItem + Send + Sync
|
||||||
|
{
|
||||||
|
fn reset(&mut self, view: Option<Arc<SrcView>>) {
|
||||||
|
let old_area = self.area();
|
||||||
|
|
||||||
|
self.src_view = view;
|
||||||
|
|
||||||
|
self.cast.notify(&old_area);
|
||||||
|
self.cast.notify(&self.src_view.area())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&mut self, area: &IndexArea<Key>) {
|
||||||
|
self.cast.notify(area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
148
nested/src/index/map_key.rs
Normal file
148
nested/src/index/map_key.rs
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
pub use {
|
||||||
|
std::{
|
||||||
|
sync::Arc,
|
||||||
|
boxed::Box
|
||||||
|
},
|
||||||
|
std::sync::RwLock,
|
||||||
|
crate::{
|
||||||
|
core::{
|
||||||
|
View,
|
||||||
|
Observer,
|
||||||
|
ObserverExt,
|
||||||
|
ObserverBroadcast,
|
||||||
|
ViewPort,
|
||||||
|
InnerViewPort,
|
||||||
|
OuterViewPort
|
||||||
|
},
|
||||||
|
index::{IndexArea, IndexView},
|
||||||
|
grid::{GridView}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
impl<SrcKey, Item> OuterViewPort<dyn IndexView<SrcKey, Item = Item>>
|
||||||
|
where SrcKey: Clone + Send + Sync + 'static,
|
||||||
|
Item: 'static
|
||||||
|
{
|
||||||
|
pub fn map_key<
|
||||||
|
DstKey: Clone + Send + Sync + 'static,
|
||||||
|
F1: Fn(&SrcKey) -> DstKey + Send + Sync + 'static,
|
||||||
|
F2: Fn(&DstKey) -> Option<SrcKey> + Send + Sync + 'static,
|
||||||
|
>(
|
||||||
|
&self,
|
||||||
|
f1: F1,
|
||||||
|
f2: F2
|
||||||
|
) -> OuterViewPort<dyn IndexView<DstKey, Item = Item>> {
|
||||||
|
let port = ViewPort::new();
|
||||||
|
port.add_update_hook(Arc::new(self.0.clone()));
|
||||||
|
|
||||||
|
let map = MapIndexKey::new(port.inner(), f1, f2);
|
||||||
|
self.add_observer(map.clone());
|
||||||
|
port.into_outer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Item> OuterViewPort<dyn IndexView<usize, Item = Item>>
|
||||||
|
where Item: 'static
|
||||||
|
{
|
||||||
|
pub fn to_grid_horizontal(&self) -> OuterViewPort<dyn GridView<Item = Item>> {
|
||||||
|
self.map_key(
|
||||||
|
|idx| cgmath::Point2::new(*idx as i16, 0),
|
||||||
|
|pt| if pt.y == 0 { Some(pt.x as usize) } else { None }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_grid_vertical(&self) -> OuterViewPort<dyn GridView<Item = Item>> {
|
||||||
|
self.map_key(
|
||||||
|
|idx| cgmath::Point2::new(0, *idx as i16),
|
||||||
|
|pt| if pt.x == 0 { Some(pt.y as usize) } else { None }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MapIndexKey<DstKey, SrcKey, SrcView, F1, F2>
|
||||||
|
where DstKey: Clone + Send + Sync,
|
||||||
|
SrcKey: Clone + Send + Sync,
|
||||||
|
SrcView: IndexView<SrcKey> + ?Sized,
|
||||||
|
F1: Fn(&SrcKey) -> DstKey + Send + Sync,
|
||||||
|
F2: Fn(&DstKey) -> Option<SrcKey> + Send + Sync,
|
||||||
|
{
|
||||||
|
src_view: Option<Arc<SrcView>>,
|
||||||
|
f1: F1,
|
||||||
|
f2: F2,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn IndexView<DstKey, Item = SrcView::Item>>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<DstKey, SrcKey, SrcView, F1, F2> MapIndexKey<DstKey, SrcKey, SrcView, F1, F2>
|
||||||
|
where DstKey: Clone + Send + Sync + 'static,
|
||||||
|
SrcKey: Clone + Send + Sync + 'static,
|
||||||
|
SrcView: IndexView<SrcKey> + ?Sized + 'static,
|
||||||
|
SrcView::Item: 'static,
|
||||||
|
F1: Fn(&SrcKey) -> DstKey + Send + Sync + 'static,
|
||||||
|
F2: Fn(&DstKey) -> Option<SrcKey> + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
fn new(
|
||||||
|
port: InnerViewPort<dyn IndexView<DstKey, Item = SrcView::Item>>,
|
||||||
|
f1: F1,
|
||||||
|
f2: F2
|
||||||
|
) -> Arc<RwLock<Self>> {
|
||||||
|
let map = Arc::new(RwLock::new(
|
||||||
|
MapIndexKey {
|
||||||
|
src_view: None,
|
||||||
|
f1,
|
||||||
|
f2,
|
||||||
|
cast: port.get_broadcast()
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
port.set_view(Some(map.clone()));
|
||||||
|
map
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<DstKey, SrcKey, SrcView, F1, F2> View for MapIndexKey<DstKey, SrcKey, SrcView, F1, F2>
|
||||||
|
where DstKey: Clone + Send + Sync,
|
||||||
|
SrcKey: Clone + Send + Sync,
|
||||||
|
SrcView: IndexView<SrcKey> + ?Sized,
|
||||||
|
F1: Fn(&SrcKey) -> DstKey + Send + Sync,
|
||||||
|
F2: Fn(&DstKey) -> Option<SrcKey> + Send + Sync,
|
||||||
|
{
|
||||||
|
type Msg = IndexArea<DstKey>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<DstKey, SrcKey, SrcView, F1, F2> IndexView<DstKey> for MapIndexKey<DstKey, SrcKey, SrcView, F1, F2>
|
||||||
|
where DstKey: Clone + Send + Sync,
|
||||||
|
SrcKey: Clone + Send + Sync,
|
||||||
|
SrcView: IndexView<SrcKey> + ?Sized,
|
||||||
|
F1: Fn(&SrcKey) -> DstKey + Send + Sync,
|
||||||
|
F2: Fn(&DstKey) -> Option<SrcKey> + Send + Sync,
|
||||||
|
{
|
||||||
|
type Item = SrcView::Item;
|
||||||
|
|
||||||
|
fn get(&self, key: &DstKey) -> Option<Self::Item> {
|
||||||
|
self.src_view.get(&(self.f2)(key)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn area(&self) -> IndexArea<DstKey> {
|
||||||
|
self.src_view.area().map(&self.f1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<DstKey, SrcKey, SrcView, F1, F2> Observer<SrcView> for MapIndexKey<DstKey, SrcKey, SrcView, F1, F2>
|
||||||
|
where DstKey: Clone + Send + Sync,
|
||||||
|
SrcKey: Clone + Send + Sync,
|
||||||
|
SrcView: IndexView<SrcKey> + ?Sized,
|
||||||
|
F1: Fn(&SrcKey) -> DstKey + Send + Sync,
|
||||||
|
F2: Fn(&DstKey) -> Option<SrcKey> + Send + Sync,
|
||||||
|
{
|
||||||
|
fn reset(&mut self, view: Option<Arc<SrcView>>) {
|
||||||
|
let old_area = self.area();
|
||||||
|
self.src_view = view;
|
||||||
|
self.cast.notify(&old_area);
|
||||||
|
self.cast.notify(&self.area());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&mut self, msg: &IndexArea<SrcKey>) {
|
||||||
|
self.cast.notify(&msg.map(&self.f1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
126
nested/src/index/mod.rs
Normal file
126
nested/src/index/mod.rs
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
|
||||||
|
pub mod map_item;
|
||||||
|
pub mod map_key;
|
||||||
|
pub mod buffer;
|
||||||
|
|
||||||
|
use {
|
||||||
|
std::{
|
||||||
|
sync::Arc,
|
||||||
|
ops::{Deref, RangeInclusive},
|
||||||
|
},
|
||||||
|
std::sync::RwLock,
|
||||||
|
crate::core::View
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum IndexArea<Key> {
|
||||||
|
Empty,
|
||||||
|
Full,
|
||||||
|
Set(Vec<Key>),
|
||||||
|
Range(RangeInclusive<Key>),
|
||||||
|
//Procedural(Arc<dyn Fn() -> Box<dyn Iterator<Item = Key>>>)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Key> IndexArea<Key> {
|
||||||
|
pub fn map<T>(&self, f: impl Fn(&Key) -> T) -> IndexArea<T> {
|
||||||
|
match self {
|
||||||
|
IndexArea::Empty => IndexArea::Empty,
|
||||||
|
IndexArea::Full => IndexArea::Full,
|
||||||
|
IndexArea::Set(v) => IndexArea::Set(v.iter().map(&f).collect()),
|
||||||
|
IndexArea::Range(r) => IndexArea::Range(f(&r.start()) ..= f(&r.end()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait IndexView<Key> : View<Msg = IndexArea<Key>>
|
||||||
|
where Key: Send + Sync {
|
||||||
|
type Item;
|
||||||
|
|
||||||
|
fn get(&self, key: &Key) -> Option<Self::Item>;
|
||||||
|
|
||||||
|
fn area(&self) -> IndexArea<Key> {
|
||||||
|
IndexArea::Full
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<Key, V> IndexView<Key> for RwLock<V>
|
||||||
|
where Key: Send + Sync,
|
||||||
|
V: IndexView<Key> + ?Sized
|
||||||
|
{
|
||||||
|
type Item = V::Item;
|
||||||
|
|
||||||
|
fn get(&self, key: &Key) -> Option<Self::Item> {
|
||||||
|
self.read().unwrap().get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn area(&self) -> IndexArea<Key> {
|
||||||
|
self.read().unwrap().area()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Key, V> IndexView<Key> for Arc<V>
|
||||||
|
where Key: Send + Sync,
|
||||||
|
V: IndexView<Key> + ?Sized
|
||||||
|
{
|
||||||
|
type Item = V::Item;
|
||||||
|
|
||||||
|
fn get(&self, key: &Key) -> Option<Self::Item> {
|
||||||
|
self.deref().get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn area(&self) -> IndexArea<Key> {
|
||||||
|
self.deref().area()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Key, V> IndexView<Key> for Option<V>
|
||||||
|
where Key: Send + Sync,
|
||||||
|
V: IndexView<Key>
|
||||||
|
{
|
||||||
|
type Item = V::Item;
|
||||||
|
|
||||||
|
fn get(&self, key: &Key) -> Option<Self::Item> {
|
||||||
|
self.as_ref()?.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn area(&self) -> IndexArea<Key> {
|
||||||
|
if let Some(v) = self.as_ref() {
|
||||||
|
v.area()
|
||||||
|
} else {
|
||||||
|
IndexArea::Empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
/*
|
||||||
|
pub trait ImplIndexView : Send + Sync {
|
||||||
|
type Key : Send + Sync;
|
||||||
|
type Value;
|
||||||
|
|
||||||
|
fn get(&self, key: &Self::Key) -> Option<Self::Value>;
|
||||||
|
fn area(&self) -> Option<Vec<Self::Key>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: ImplIndexView> View for V {
|
||||||
|
type Msg = V::Key;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: ImplIndexView> IndexView<V::Key> for V {
|
||||||
|
type Item = V::Value;
|
||||||
|
|
||||||
|
fn get(&self, key: &V::Key) -> Option<Self::Item> {
|
||||||
|
(self as &V).get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn area(&self) -> Option<Vec<V::Key>> {
|
||||||
|
(self as &V).area()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
76
nested/src/integer/add.rs
Normal file
76
nested/src/integer/add.rs
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
use {
|
||||||
|
std::sync::{Arc, RwLock},
|
||||||
|
crate::{
|
||||||
|
core::{InnerViewPort, OuterViewPort},
|
||||||
|
sequence::SequenceView,
|
||||||
|
vec::VecBuffer,
|
||||||
|
projection::ProjectionHelper
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fn posint_add(
|
||||||
|
radix: usize,
|
||||||
|
a: impl SequenceView<Item = usize>,
|
||||||
|
b: impl SequenceView<Item = usize>
|
||||||
|
) -> Vec<usize> {
|
||||||
|
let mut carry = 0;
|
||||||
|
let mut result = Vec::new();
|
||||||
|
|
||||||
|
for digit_idx in 0 .. std::cmp::max(a.len().unwrap_or(0), b.len().unwrap_or(0)) {
|
||||||
|
let sum =
|
||||||
|
a.get(&digit_idx).unwrap_or(0) +
|
||||||
|
b.get(&digit_idx).unwrap_or(0) +
|
||||||
|
carry;
|
||||||
|
|
||||||
|
result.push(sum % radix);
|
||||||
|
|
||||||
|
carry =
|
||||||
|
if sum > radix {
|
||||||
|
sum - radix
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if carry > 0 {
|
||||||
|
result.push(carry);
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Add {
|
||||||
|
radix: usize,
|
||||||
|
a: Arc<dyn SequenceView<Item = usize>>, // PosInt, Little Endian
|
||||||
|
b: Arc<dyn SequenceView<Item = usize>>, // PosInt, Little Endian
|
||||||
|
c: VecBuffer<usize>,
|
||||||
|
_proj_helper: ProjectionHelper<usize, Self>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add {
|
||||||
|
pub fn new(
|
||||||
|
radix: usize,
|
||||||
|
a: OuterViewPort<dyn SequenceView<Item = usize>>,
|
||||||
|
b: OuterViewPort<dyn SequenceView<Item = usize>>,
|
||||||
|
c: InnerViewPort<RwLock<Vec<usize>>>//<dyn SequenceView<Item = usize>>
|
||||||
|
) -> Arc<RwLock<Self>> {
|
||||||
|
let mut proj_helper = ProjectionHelper::new(c.0.update_hooks.clone());
|
||||||
|
let add = Arc::new(RwLock::new(
|
||||||
|
Add {
|
||||||
|
radix,
|
||||||
|
a: proj_helper.new_sequence_arg(0, a, |s: &mut Self, _digit_idx| s.update()),
|
||||||
|
b: proj_helper.new_sequence_arg(1, b, |s: &mut Self, _digit_idx| s.update()),
|
||||||
|
c: VecBuffer::new(c),
|
||||||
|
_proj_helper: proj_helper
|
||||||
|
}
|
||||||
|
));
|
||||||
|
add
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self) {
|
||||||
|
self.c.clear();
|
||||||
|
for digit in posint_add(self.radix, self.a.clone(), self.b.clone()) {
|
||||||
|
self.c.push(digit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
161
nested/src/integer/editor.rs
Normal file
161
nested/src/integer/editor.rs
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
use {
|
||||||
|
std::sync::Arc,
|
||||||
|
std::sync::RwLock,
|
||||||
|
termion::event::{Key, Event},
|
||||||
|
cgmath::Point2,
|
||||||
|
crate::{
|
||||||
|
core::{ViewPort, OuterViewPort, Observer},
|
||||||
|
singleton::{SingletonView, SingletonBuffer},
|
||||||
|
sequence::{SequenceView, SequenceViewExt},
|
||||||
|
vec::VecBuffer,
|
||||||
|
terminal::{TerminalAtom, TerminalStyle, TerminalView, TerminalEvent, TerminalEditor, TerminalEditorResult},
|
||||||
|
tree_nav::{TreeNav, TreeNavResult, TerminalTreeEditor, TreeCursor},
|
||||||
|
list::{ListEditor, ListEditorStyle, sexpr::ListDecoration}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub struct DigitEditor {
|
||||||
|
radix: u32,
|
||||||
|
data: SingletonBuffer<Option<char>>,
|
||||||
|
data_port: ViewPort<dyn SingletonView<Item = Option<char>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DigitEditor {
|
||||||
|
pub fn new(radix: u32) -> Self {
|
||||||
|
let mut data_port = ViewPort::new();
|
||||||
|
DigitEditor {
|
||||||
|
radix,
|
||||||
|
data: SingletonBuffer::new(None, data_port.inner()),
|
||||||
|
data_port
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_data_port(&self) -> OuterViewPort<dyn SingletonView<Item = Option<u32>>> {
|
||||||
|
let radix = self.radix;
|
||||||
|
self.data_port.outer().map(
|
||||||
|
move |c| c?.to_digit(radix)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TreeNav for DigitEditor {}
|
||||||
|
impl TerminalEditor for DigitEditor {
|
||||||
|
fn get_term_view(&self) -> OuterViewPort<dyn TerminalView> {
|
||||||
|
let radix = self.radix;
|
||||||
|
self.data_port.outer().map(
|
||||||
|
move |c| TerminalAtom::new(
|
||||||
|
c.unwrap_or('?'),
|
||||||
|
if c.unwrap_or('?').to_digit(radix).is_some() {
|
||||||
|
TerminalStyle::fg_color((100, 140, 100))
|
||||||
|
} else {
|
||||||
|
//TerminalStyle::bg_color((90, 10, 10))
|
||||||
|
TerminalStyle::fg_color((200, 40, 40))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
).to_grid()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_terminal_event(&mut self, event: &TerminalEvent) -> TerminalEditorResult {
|
||||||
|
match event {
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Char(' '))) |
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Char('\n'))) =>
|
||||||
|
TerminalEditorResult::Exit,
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Char(c))) => {
|
||||||
|
self.data.set(Some(*c));
|
||||||
|
TerminalEditorResult::Exit
|
||||||
|
}
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Backspace)) |
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Delete)) => {
|
||||||
|
self.data.set(None);
|
||||||
|
TerminalEditorResult::Exit
|
||||||
|
}
|
||||||
|
_ => TerminalEditorResult::Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PosIntEditor {
|
||||||
|
radix: u32,
|
||||||
|
digits_editor: ListEditor< DigitEditor,
|
||||||
|
Box<dyn Fn() -> Arc<RwLock<DigitEditor>> + Send + Sync + 'static> >
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PosIntEditor {
|
||||||
|
pub fn new(radix: u32) -> Self {
|
||||||
|
PosIntEditor {
|
||||||
|
radix,
|
||||||
|
digits_editor: ListEditor::new(
|
||||||
|
Box::new(
|
||||||
|
move || {
|
||||||
|
Arc::new(RwLock::new(DigitEditor::new(radix)))
|
||||||
|
}
|
||||||
|
),
|
||||||
|
crate::list::ListEditorStyle::Hex
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_data_port(&self) -> OuterViewPort<dyn SequenceView<Item = u32>> {
|
||||||
|
let radix = self.radix;
|
||||||
|
self.digits_editor.get_data_port()
|
||||||
|
.filter_map(move |digit_editor| digit_editor.read().unwrap().data.get()?.to_digit(radix))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_value(&self) -> u32 {
|
||||||
|
let mut value = 0;
|
||||||
|
let mut weight = 1;
|
||||||
|
for digit_value in self.get_data_port().get_view().unwrap().iter().collect::<Vec<_>>().into_iter().rev() {
|
||||||
|
value += digit_value * weight;
|
||||||
|
weight *= self.radix;
|
||||||
|
}
|
||||||
|
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TreeNav for PosIntEditor {
|
||||||
|
fn get_cursor(&self) -> TreeCursor { self.digits_editor.get_cursor() }
|
||||||
|
fn goto(&mut self, cur: TreeCursor) -> TreeNavResult { self.digits_editor.goto(cur) }
|
||||||
|
fn goto_home(&mut self) -> TreeNavResult { self.digits_editor.goto_home() }
|
||||||
|
fn goto_end(&mut self) -> TreeNavResult { self.digits_editor.goto_end() }
|
||||||
|
fn pxev(&mut self) -> TreeNavResult { self.digits_editor.pxev() }
|
||||||
|
fn nexd(&mut self) -> TreeNavResult { self.digits_editor.nexd() }
|
||||||
|
fn up(&mut self) -> TreeNavResult { self.digits_editor.up() }
|
||||||
|
fn dn(&mut self) -> TreeNavResult { self.digits_editor.dn() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TerminalEditor for PosIntEditor {
|
||||||
|
fn get_term_view(&self) -> OuterViewPort<dyn TerminalView> {
|
||||||
|
self.digits_editor
|
||||||
|
.get_seg_seq_view()
|
||||||
|
.decorate(
|
||||||
|
match self.radix {
|
||||||
|
2 => "0b",
|
||||||
|
8 => "0o",
|
||||||
|
10 => "0d",
|
||||||
|
16 => "0x",
|
||||||
|
_ => ""
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
0
|
||||||
|
)
|
||||||
|
.to_grid_horizontal()
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_terminal_event(&mut self, event: &TerminalEvent) -> TerminalEditorResult {
|
||||||
|
match event {
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Char(' '))) |
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Char('\n'))) => {
|
||||||
|
self.digits_editor.up();
|
||||||
|
TerminalEditorResult::Exit
|
||||||
|
}
|
||||||
|
|
||||||
|
event => self.digits_editor.handle_terminal_event(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
|
|
||||||
|
pub mod radix;
|
||||||
pub mod add;
|
pub mod add;
|
||||||
pub mod editor;
|
pub mod editor;
|
||||||
pub mod radix;
|
|
||||||
pub mod ctx;
|
|
||||||
|
|
||||||
pub use {
|
pub use {
|
||||||
add::Add,
|
|
||||||
editor::{DigitEditor, PosIntEditor},
|
|
||||||
radix::RadixProjection,
|
radix::RadixProjection,
|
||||||
ctx::init_ctx
|
add::Add,
|
||||||
|
editor::{DigitEditor, PosIntEditor}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,44 +1,39 @@
|
||||||
use {
|
use {
|
||||||
r3vi::{
|
|
||||||
view::{
|
|
||||||
InnerViewPort, Observer, OuterViewPort,
|
|
||||||
sequence::*,
|
|
||||||
},
|
|
||||||
buffer::{vec::*}
|
|
||||||
},
|
|
||||||
std::sync::{Arc, RwLock},
|
std::sync::{Arc, RwLock},
|
||||||
|
crate::{
|
||||||
|
core::{
|
||||||
|
Observer,
|
||||||
|
InnerViewPort,
|
||||||
|
OuterViewPort
|
||||||
|
},
|
||||||
|
sequence::SequenceView,
|
||||||
|
vec::VecBuffer
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct RadixProjection {
|
pub struct RadixProjection {
|
||||||
src_radix: usize,
|
src_radix: usize,
|
||||||
dst_radix: usize,
|
dst_radix: usize,
|
||||||
src_digits: Option<Arc<dyn SequenceView<Item = usize>>>,
|
src_digits: Option<Arc<dyn SequenceView<Item = usize>>>,
|
||||||
dst_digits: RwLock<VecBuffer<usize>>,
|
dst_digits: RwLock<VecBuffer<usize>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RadixProjection {
|
impl RadixProjection {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
// static parameters
|
|
||||||
//---
|
|
||||||
src_radix: usize,
|
src_radix: usize,
|
||||||
dst_radix: usize,
|
dst_radix: usize,
|
||||||
//---
|
|
||||||
|
|
||||||
// dynamic parameters
|
|
||||||
//---
|
|
||||||
// input
|
|
||||||
src_digits: OuterViewPort<dyn SequenceView<Item = usize>>,
|
src_digits: OuterViewPort<dyn SequenceView<Item = usize>>,
|
||||||
// output
|
dst_digits: InnerViewPort<RwLock<Vec<usize>>>
|
||||||
dst_digits: InnerViewPort<RwLock<Vec<usize>>>,
|
|
||||||
//---
|
|
||||||
) -> Arc<RwLock<Self>> {
|
) -> Arc<RwLock<Self>> {
|
||||||
dst_digits.0.add_update_hook(Arc::new(src_digits.0.clone()));
|
dst_digits.0.add_update_hook(Arc::new(src_digits.0.clone()));
|
||||||
let proj = Arc::new(RwLock::new(RadixProjection {
|
let proj = Arc::new(RwLock::new(
|
||||||
src_radix,
|
RadixProjection {
|
||||||
dst_radix,
|
src_radix,
|
||||||
src_digits: None,
|
dst_radix,
|
||||||
dst_digits: RwLock::new(VecBuffer::with_port(dst_digits)),
|
src_digits: None,
|
||||||
}));
|
dst_digits: RwLock::new(VecBuffer::new(dst_digits))
|
||||||
|
}
|
||||||
|
));
|
||||||
src_digits.add_observer(proj.clone());
|
src_digits.add_observer(proj.clone());
|
||||||
proj
|
proj
|
||||||
}
|
}
|
||||||
|
@ -46,7 +41,7 @@ impl RadixProjection {
|
||||||
fn machine_int(&self) -> usize {
|
fn machine_int(&self) -> usize {
|
||||||
let mut val = 0;
|
let mut val = 0;
|
||||||
let mut r = 1;
|
let mut r = 1;
|
||||||
for i in 0..self.src_digits.len().unwrap_or(0) {
|
for i in 0 .. self.src_digits.len().unwrap_or(0) {
|
||||||
val += r * self.src_digits.get(&i).unwrap();
|
val += r * self.src_digits.get(&i).unwrap();
|
||||||
r *= self.src_radix;
|
r *= self.src_radix;
|
||||||
}
|
}
|
||||||
|
@ -69,18 +64,18 @@ impl RadixProjection {
|
||||||
|
|
||||||
fn _update_dst_digit(&mut self, _idx: usize) {
|
fn _update_dst_digit(&mut self, _idx: usize) {
|
||||||
/*
|
/*
|
||||||
let v = 0; // calculate new digit value
|
let v = 0; // calculate new digit value
|
||||||
|
|
||||||
// which src-digits are responsible?
|
// which src-digits are responsible?
|
||||||
|
|
||||||
if idx < self.dst_digits.len() {
|
if idx < self.dst_digits.len() {
|
||||||
self.dst_digits.get_mut(idx) = v;
|
self.dst_digits.get_mut(idx) = v;
|
||||||
} else if idx == self.dst_digits.len() {
|
} else if idx == self.dst_digits.len() {
|
||||||
self.dst_digits.push(v);
|
self.dst_digits.push(v);
|
||||||
} else {
|
} else {
|
||||||
// error
|
// error
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +1,26 @@
|
||||||
#![feature(trait_alias)]
|
#![feature(trait_alias)]
|
||||||
|
|
||||||
|
pub mod core;
|
||||||
|
pub mod projection;
|
||||||
|
|
||||||
|
pub mod singleton;
|
||||||
|
pub mod index;
|
||||||
|
pub mod grid;
|
||||||
|
pub mod sequence;
|
||||||
|
pub mod vec;
|
||||||
pub mod terminal;
|
pub mod terminal;
|
||||||
|
pub mod integer;
|
||||||
|
pub mod list;
|
||||||
|
|
||||||
pub mod utils;
|
pub mod tree_nav;
|
||||||
pub mod editors;
|
|
||||||
pub mod tree;
|
|
||||||
pub mod type_system;
|
|
||||||
|
|
||||||
pub mod diagnostics;
|
pub mod string_editor;
|
||||||
pub mod commander;
|
|
||||||
//pub mod product;
|
pub mod bimap;
|
||||||
//pub mod sum;
|
|
||||||
//pub mod list;
|
pub mod sdf;
|
||||||
|
|
||||||
pub fn magic_header() {
|
pub fn magic_header() {
|
||||||
eprintln!("<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>");
|
eprintln!("<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
pub trait StringGen {
|
|
||||||
fn get_string(&self) -> String;
|
|
||||||
}
|
|
||||||
|
|
||||||
use crate::{tree::{TreeNav}, diagnostics::Diagnostics, terminal::TerminalView, core::{OuterViewPort}};
|
|
||||||
*/
|
|
||||||
|
|
||||||
use r3vi::view::OuterViewPort;
|
|
||||||
use crate::terminal::TerminalView;
|
|
||||||
|
|
||||||
pub trait PtySegment {
|
|
||||||
fn pty_view(&self) -> OuterViewPort<dyn TerminalView>;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
35
nested/src/list/cursor.rs
Normal file
35
nested/src/list/cursor.rs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||||
|
pub enum ListCursorMode {
|
||||||
|
Insert,
|
||||||
|
Select,
|
||||||
|
Modify
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||||
|
pub struct ListCursor {
|
||||||
|
pub mode: ListCursorMode,
|
||||||
|
pub idx: Option<usize>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ListCursor {
|
||||||
|
fn default() -> Self {
|
||||||
|
ListCursor {
|
||||||
|
mode: ListCursorMode::Select,
|
||||||
|
idx: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
pub trait ListNav {
|
||||||
|
fn pxev(&mut self) -> ListNavResult;
|
||||||
|
fn nexd(&mut self) -> ListNavResult;
|
||||||
|
fn pua(&mut self) -> ListNavResult;
|
||||||
|
fn end(&mut self) -> ListNavResult;
|
||||||
|
|
||||||
|
fn set_cursor(&mut self, new_cursor: Option<ListCursor>) -> ListNavResult;
|
||||||
|
fn get_cursor(&self) -> Option<ListCursor>;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
757
nested/src/list/editor.rs
Normal file
757
nested/src/list/editor.rs
Normal file
|
@ -0,0 +1,757 @@
|
||||||
|
use {
|
||||||
|
std::sync::{Arc, RwLock},
|
||||||
|
termion::event::{Event, Key},
|
||||||
|
crate::{
|
||||||
|
core::{
|
||||||
|
ViewPort,
|
||||||
|
OuterViewPort,
|
||||||
|
},
|
||||||
|
singleton::{SingletonView, SingletonBuffer},
|
||||||
|
sequence::{SequenceView},
|
||||||
|
vec::{VecBuffer},
|
||||||
|
terminal::{
|
||||||
|
TerminalView,
|
||||||
|
TerminalStyle,
|
||||||
|
TerminalEvent,
|
||||||
|
TerminalEditor,
|
||||||
|
TerminalEditorResult,
|
||||||
|
make_label
|
||||||
|
},
|
||||||
|
list::{SExprView, ListDecoration, ListCursor, ListCursorMode, editor_view::{ListEditorView, ListEditorViewSegment}},
|
||||||
|
tree_nav::{TreeCursor, TreeNav, TreeNavResult, TerminalTreeEditor}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum ListEditorStyle {
|
||||||
|
HorizontalSexpr,
|
||||||
|
VerticalSexpr,
|
||||||
|
Path,
|
||||||
|
String,
|
||||||
|
Clist,
|
||||||
|
Hex,
|
||||||
|
Plain
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ListEditor<ItemEditor, FnMakeItemEditor>
|
||||||
|
where ItemEditor: TerminalEditor + ?Sized + Send + Sync + 'static,
|
||||||
|
FnMakeItemEditor: Fn() -> Arc<RwLock<ItemEditor>>
|
||||||
|
{
|
||||||
|
cursor: SingletonBuffer<ListCursor>,
|
||||||
|
data: VecBuffer<Arc<RwLock<ItemEditor>>>,
|
||||||
|
|
||||||
|
cursor_port: ViewPort<dyn SingletonView<Item = ListCursor>>,
|
||||||
|
data_port: ViewPort<RwLock<Vec<Arc<RwLock<ItemEditor>>>>>,
|
||||||
|
|
||||||
|
make_item_editor: FnMakeItemEditor,
|
||||||
|
|
||||||
|
style: ListEditorStyle,
|
||||||
|
level: usize,
|
||||||
|
cur_dist: Arc<RwLock<usize>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<ItemEditor, FnMakeItemEditor> TreeNav for ListEditor<ItemEditor, FnMakeItemEditor>
|
||||||
|
where ItemEditor: TerminalTreeEditor + ?Sized + Send + Sync + 'static,
|
||||||
|
FnMakeItemEditor: Fn() -> Arc<RwLock<ItemEditor>>
|
||||||
|
{
|
||||||
|
fn get_cursor(&self) -> TreeCursor {
|
||||||
|
let cur = self.cursor.get();
|
||||||
|
match cur.mode {
|
||||||
|
ListCursorMode::Insert |
|
||||||
|
ListCursorMode::Select => {
|
||||||
|
TreeCursor {
|
||||||
|
leaf_mode: cur.mode,
|
||||||
|
tree_addr: if let Some(i) = cur.idx { vec![ i ] } else { vec![] }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ListCursorMode::Modify => {
|
||||||
|
if let Some(i) = cur.idx {
|
||||||
|
if i < self.data.len() {
|
||||||
|
let mut sub_cur = self.data.get(i).read().unwrap().get_cursor();
|
||||||
|
sub_cur.tree_addr.insert(0, i);
|
||||||
|
return sub_cur;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TreeCursor {
|
||||||
|
leaf_mode: cur.mode,
|
||||||
|
tree_addr: vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn goto(&mut self, new_cur: TreeCursor) -> TreeNavResult {
|
||||||
|
let old_cur = self.cursor.get();
|
||||||
|
if old_cur.mode == ListCursorMode::Modify {
|
||||||
|
if let Some(i) = old_cur.idx {
|
||||||
|
let ce = self.data.get_mut(i);
|
||||||
|
let mut cur_edit = ce.write().unwrap();
|
||||||
|
cur_edit.goto(TreeCursor::default());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if new_cur.tree_addr.len() == 1 {
|
||||||
|
self.cursor.set(ListCursor{
|
||||||
|
mode: new_cur.leaf_mode,
|
||||||
|
idx: Some(new_cur.tree_addr[0])
|
||||||
|
});
|
||||||
|
TreeNavResult::Continue
|
||||||
|
} else if new_cur.tree_addr.len() > 1 && new_cur.tree_addr[0] < self.data.len() {
|
||||||
|
self.cursor.set(ListCursor {
|
||||||
|
mode: ListCursorMode::Modify,
|
||||||
|
idx: Some(new_cur.tree_addr[0])
|
||||||
|
});
|
||||||
|
|
||||||
|
let ne = self.data.get_mut(new_cur.tree_addr[0]);
|
||||||
|
let mut nxt_edit = ne.write().unwrap();
|
||||||
|
|
||||||
|
nxt_edit.goto(
|
||||||
|
TreeCursor {
|
||||||
|
leaf_mode: new_cur.leaf_mode,
|
||||||
|
tree_addr: new_cur.tree_addr[1..].iter().cloned().collect()
|
||||||
|
});
|
||||||
|
|
||||||
|
TreeNavResult::Continue
|
||||||
|
} else {
|
||||||
|
self.cursor.set(ListCursor {
|
||||||
|
mode: new_cur.leaf_mode,
|
||||||
|
idx: None
|
||||||
|
});
|
||||||
|
TreeNavResult::Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn goto_end(&mut self) -> TreeNavResult {
|
||||||
|
let mut cur = self.cursor.get();
|
||||||
|
let i = cur.idx.unwrap_or(0);
|
||||||
|
|
||||||
|
if self.data.len() == 0 && cur.idx.is_none() {
|
||||||
|
self.cursor.set(
|
||||||
|
ListCursor {
|
||||||
|
mode: ListCursorMode::Insert,
|
||||||
|
idx: Some(0)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return TreeNavResult::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if i < self.data.len() {
|
||||||
|
match cur.mode {
|
||||||
|
ListCursorMode::Insert => {
|
||||||
|
if i < self.data.len() || cur.idx.is_none() {
|
||||||
|
cur.idx = Some(self.data.len());
|
||||||
|
self.cursor.set(cur);
|
||||||
|
TreeNavResult::Continue
|
||||||
|
} else {
|
||||||
|
self.cursor.set(ListCursor::default());
|
||||||
|
TreeNavResult::Exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ListCursorMode::Select => {
|
||||||
|
if self.data.len() == 0 && cur.idx.is_none() {
|
||||||
|
self.cursor.set(
|
||||||
|
ListCursor {
|
||||||
|
mode: ListCursorMode::Insert,
|
||||||
|
idx: Some(0)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return TreeNavResult::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if i+1 < self.data.len() || cur.idx.is_none() {
|
||||||
|
cur.idx = Some(self.data.len()-1);
|
||||||
|
self.cursor.set(cur);
|
||||||
|
TreeNavResult::Continue
|
||||||
|
} else {
|
||||||
|
self.cursor.set(ListCursor::default());
|
||||||
|
TreeNavResult::Exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ListCursorMode::Modify => {
|
||||||
|
let ce = self.data.get_mut(i);
|
||||||
|
let mut cur_edit = ce.write().unwrap();
|
||||||
|
let cur_mode = cur_edit.get_cursor().leaf_mode;
|
||||||
|
let depth = cur_edit.get_cursor().tree_addr.len();
|
||||||
|
match cur_edit.goto_end() {
|
||||||
|
TreeNavResult::Continue => {
|
||||||
|
TreeNavResult::Continue
|
||||||
|
}
|
||||||
|
TreeNavResult::Exit => {
|
||||||
|
drop(cur_edit);
|
||||||
|
|
||||||
|
self.up();
|
||||||
|
|
||||||
|
if i+1 < self.data.len() {
|
||||||
|
self.set_mode(ListCursorMode::Select);
|
||||||
|
self.nexd();
|
||||||
|
|
||||||
|
for x in 1 .. depth {
|
||||||
|
self.dn();
|
||||||
|
self.goto_home();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.set_leaf_mode(cur_mode);
|
||||||
|
self.dn();
|
||||||
|
self.goto_end();
|
||||||
|
|
||||||
|
return TreeNavResult::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNavResult::Exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.cursor.set(ListCursor::default());
|
||||||
|
TreeNavResult::Exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn goto_home(&mut self) -> TreeNavResult {
|
||||||
|
let cur = self.cursor.get();
|
||||||
|
if self.data.len() == 0 && cur.idx.is_none() {
|
||||||
|
self.cursor.set(
|
||||||
|
ListCursor {
|
||||||
|
mode: ListCursorMode::Insert,
|
||||||
|
idx: Some(0)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return TreeNavResult::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
match cur.mode {
|
||||||
|
ListCursorMode::Insert |
|
||||||
|
ListCursorMode::Select => {
|
||||||
|
if cur.idx != Some(0) {
|
||||||
|
self.cursor.set(
|
||||||
|
ListCursor {
|
||||||
|
mode: if self.data.len() == 0 { ListCursorMode::Insert } else { cur.mode },
|
||||||
|
idx: Some(0)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
TreeNavResult::Continue
|
||||||
|
} else {
|
||||||
|
self.cursor.set(ListCursor::default());
|
||||||
|
TreeNavResult::Exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ListCursorMode::Modify => {
|
||||||
|
let ce = self.get_item().unwrap();
|
||||||
|
let mut cur_edit = ce.write().unwrap();
|
||||||
|
let cur_mode = cur_edit.get_cursor().leaf_mode;
|
||||||
|
let depth = cur_edit.get_cursor().tree_addr.len();
|
||||||
|
|
||||||
|
match cur_edit.goto_home() {
|
||||||
|
TreeNavResult::Exit => {
|
||||||
|
drop(cur_edit);
|
||||||
|
|
||||||
|
if let Some(i) = cur.idx {
|
||||||
|
self.up();
|
||||||
|
|
||||||
|
if i > 0 {
|
||||||
|
self.set_mode(ListCursorMode::Select);
|
||||||
|
self.pxev();
|
||||||
|
|
||||||
|
for x in 1 .. depth {
|
||||||
|
self.dn();
|
||||||
|
self.goto_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.set_leaf_mode(cur_mode);
|
||||||
|
self.dn();
|
||||||
|
self.goto_home();
|
||||||
|
return TreeNavResult::Continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNavResult::Exit
|
||||||
|
}
|
||||||
|
TreeNavResult::Continue => TreeNavResult::Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn up(&mut self) -> TreeNavResult {
|
||||||
|
let cur = self.cursor.get();
|
||||||
|
if cur.mode == ListCursorMode::Modify {
|
||||||
|
if let Some(i) = cur.idx {
|
||||||
|
let ce = self.data.get_mut(i);
|
||||||
|
let mut cur_edit = ce.write().unwrap();
|
||||||
|
let mode = cur_edit.get_cursor().leaf_mode;
|
||||||
|
|
||||||
|
match cur_edit.up() {
|
||||||
|
TreeNavResult::Exit => {
|
||||||
|
self.set_mode(mode);
|
||||||
|
}
|
||||||
|
TreeNavResult::Continue => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TreeNavResult::Continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cursor.set(ListCursor {
|
||||||
|
mode: cur.mode,
|
||||||
|
idx: None
|
||||||
|
});
|
||||||
|
TreeNavResult::Exit
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dn(&mut self) -> TreeNavResult {
|
||||||
|
let cur = self.cursor.get();
|
||||||
|
match cur.mode {
|
||||||
|
ListCursorMode::Insert |
|
||||||
|
ListCursorMode::Select => {
|
||||||
|
if let Some(i) = cur.idx {
|
||||||
|
if i < self.data.len() {
|
||||||
|
self.set_mode(ListCursorMode::Modify);
|
||||||
|
self.data.get_mut(i).write().unwrap().goto(
|
||||||
|
TreeCursor {
|
||||||
|
leaf_mode: cur.mode,
|
||||||
|
tree_addr: vec![]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
*self.cur_dist.write().unwrap() += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TreeNavResult::Continue
|
||||||
|
}
|
||||||
|
ListCursorMode::Modify => {
|
||||||
|
self.get_item().unwrap().write().unwrap().dn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pxev(&mut self) -> TreeNavResult {
|
||||||
|
let mut cur = self.cursor.get();
|
||||||
|
if let Some(i) = cur.idx {
|
||||||
|
match cur.mode {
|
||||||
|
ListCursorMode::Insert |
|
||||||
|
ListCursorMode::Select => {
|
||||||
|
if i > 0 {
|
||||||
|
cur.idx = Some(i - 1);
|
||||||
|
self.cursor.set(cur);
|
||||||
|
TreeNavResult::Continue
|
||||||
|
} else {
|
||||||
|
self.cursor.set(ListCursor::default());
|
||||||
|
TreeNavResult::Exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ListCursorMode::Modify => {
|
||||||
|
let ce = self.get_item().unwrap();
|
||||||
|
let mut cur_edit = ce.write().unwrap();
|
||||||
|
|
||||||
|
let cur_mode = cur_edit.get_cursor().leaf_mode;
|
||||||
|
let depth = cur_edit.get_cursor().tree_addr.len();
|
||||||
|
|
||||||
|
match cur_edit.pxev() {
|
||||||
|
TreeNavResult::Exit => {
|
||||||
|
drop(cur_edit);
|
||||||
|
self.up();
|
||||||
|
|
||||||
|
if i > 0 {
|
||||||
|
self.set_mode(ListCursorMode::Select);
|
||||||
|
self.pxev();
|
||||||
|
|
||||||
|
for x in 1 .. depth {
|
||||||
|
self.dn();
|
||||||
|
self.goto_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.set_leaf_mode(cur_mode);
|
||||||
|
self.dn();
|
||||||
|
self.goto_end();
|
||||||
|
TreeNavResult::Continue
|
||||||
|
} else {
|
||||||
|
TreeNavResult::Exit
|
||||||
|
}
|
||||||
|
},
|
||||||
|
TreeNavResult::Continue => TreeNavResult::Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TreeNavResult::Exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nexd(&mut self) -> TreeNavResult {
|
||||||
|
let mut cur = self.cursor.get();
|
||||||
|
if let Some(i) = cur.idx {
|
||||||
|
match cur.mode {
|
||||||
|
ListCursorMode::Insert => {
|
||||||
|
if i < self.data.len() {
|
||||||
|
cur.idx = Some(i + 1);
|
||||||
|
self.cursor.set(cur);
|
||||||
|
TreeNavResult::Continue
|
||||||
|
} else {
|
||||||
|
self.cursor.set(ListCursor::default());
|
||||||
|
TreeNavResult::Exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ListCursorMode::Select => {
|
||||||
|
if i+1 < self.data.len() {
|
||||||
|
cur.idx = Some(i + 1);
|
||||||
|
self.cursor.set(cur);
|
||||||
|
TreeNavResult::Continue
|
||||||
|
} else {
|
||||||
|
self.cursor.set(ListCursor::default());
|
||||||
|
TreeNavResult::Exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ListCursorMode::Modify => {
|
||||||
|
let ce = self.data.get(i);
|
||||||
|
let mut cur_edit = ce.write().unwrap();
|
||||||
|
|
||||||
|
let depth = cur_edit.get_cursor().tree_addr.len();
|
||||||
|
let cur_mode = cur_edit.get_cursor().leaf_mode;
|
||||||
|
|
||||||
|
match cur_edit.nexd() {
|
||||||
|
TreeNavResult::Exit => {
|
||||||
|
drop(cur_edit);
|
||||||
|
drop(ce);
|
||||||
|
self.up();
|
||||||
|
|
||||||
|
if i+1 < self.data.len() {
|
||||||
|
|
||||||
|
self.set_mode(ListCursorMode::Select);
|
||||||
|
self.nexd();
|
||||||
|
|
||||||
|
for x in 1 .. depth {
|
||||||
|
self.dn();
|
||||||
|
self.goto_home();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.set_leaf_mode(cur_mode);
|
||||||
|
self.dn();
|
||||||
|
self.goto_home();
|
||||||
|
TreeNavResult::Continue
|
||||||
|
} else {
|
||||||
|
TreeNavResult::Exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TreeNavResult::Continue => TreeNavResult::Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TreeNavResult::Exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<ItemEditor, FnMakeItemEditor> TerminalEditor for ListEditor<ItemEditor, FnMakeItemEditor>
|
||||||
|
where ItemEditor: TerminalTreeEditor + ?Sized + Send + Sync + 'static,
|
||||||
|
FnMakeItemEditor: Fn() -> Arc<RwLock<ItemEditor>>
|
||||||
|
{
|
||||||
|
fn get_term_view(&self) -> OuterViewPort<dyn TerminalView> {
|
||||||
|
match self.style {
|
||||||
|
ListEditorStyle::HorizontalSexpr => self.horizontal_sexpr_view(),
|
||||||
|
ListEditorStyle::VerticalSexpr => self.vertical_sexpr_view(),
|
||||||
|
ListEditorStyle::Path => self.path_view(),
|
||||||
|
ListEditorStyle::String => self.string_view(),
|
||||||
|
ListEditorStyle::Clist => self.clist_view(),
|
||||||
|
ListEditorStyle::Hex => self.hex_view(),
|
||||||
|
ListEditorStyle::Plain => self.plain_view()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_terminal_event(&mut self, event: &TerminalEvent) -> TerminalEditorResult {
|
||||||
|
let mut cur = self.cursor.get();
|
||||||
|
if let Some(idx) = cur.idx {
|
||||||
|
match cur.mode {
|
||||||
|
ListCursorMode::Insert => {
|
||||||
|
match event {
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Backspace)) => {
|
||||||
|
if idx > 0 && idx <= self.data.len() {
|
||||||
|
cur.idx = Some(idx-1);
|
||||||
|
self.cursor.set(cur);
|
||||||
|
self.data.remove(idx-1);
|
||||||
|
TerminalEditorResult::Continue
|
||||||
|
} else {
|
||||||
|
TerminalEditorResult::Exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Delete)) => {
|
||||||
|
if idx < self.data.len() {
|
||||||
|
self.data.remove(idx);
|
||||||
|
TerminalEditorResult::Continue
|
||||||
|
} else {
|
||||||
|
TerminalEditorResult::Exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Char('\t'))) |
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Insert)) => {
|
||||||
|
self.set_mode(ListCursorMode::Select);
|
||||||
|
TerminalEditorResult::Continue
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let new_edit = (self.make_item_editor)();
|
||||||
|
self.data.insert(idx, new_edit.clone());
|
||||||
|
self.dn();
|
||||||
|
self.goto_home();
|
||||||
|
|
||||||
|
match new_edit.write().unwrap().handle_terminal_event(event) {
|
||||||
|
TerminalEditorResult::Exit => {
|
||||||
|
self.cursor.set(ListCursor {
|
||||||
|
mode: ListCursorMode::Insert,
|
||||||
|
idx: Some(idx+1)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
TerminalEditorResult::Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ListCursorMode::Select => {
|
||||||
|
match event {
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Char('\t'))) |
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Insert)) => {
|
||||||
|
self.set_mode(ListCursorMode::Insert);
|
||||||
|
TerminalEditorResult::Continue
|
||||||
|
}
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Delete)) => {
|
||||||
|
self.data.remove(idx);
|
||||||
|
|
||||||
|
if self.data.len() == 0 {
|
||||||
|
self.cursor.set(ListCursor::default());
|
||||||
|
} else if idx == self.data.len() {
|
||||||
|
self.cursor.set(ListCursor {
|
||||||
|
mode: ListCursorMode::Select,
|
||||||
|
idx: Some(idx-1)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
TerminalEditorResult::Continue
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
TerminalEditorResult::Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ListCursorMode::Modify => {
|
||||||
|
let mut ce = self.data.get_mut(idx);
|
||||||
|
let mut cur_edit = ce.write().unwrap();
|
||||||
|
|
||||||
|
match cur_edit.handle_terminal_event(event) {
|
||||||
|
TerminalEditorResult::Exit => {
|
||||||
|
cur_edit.up();
|
||||||
|
drop(cur_edit);
|
||||||
|
drop(ce);
|
||||||
|
|
||||||
|
match event {
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Backspace)) => {
|
||||||
|
// todo: join instead of remove
|
||||||
|
self.cursor.set(ListCursor {
|
||||||
|
mode: ListCursorMode::Insert,
|
||||||
|
idx: Some(idx)
|
||||||
|
});
|
||||||
|
|
||||||
|
self.data.remove(idx);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// todo: split
|
||||||
|
|
||||||
|
self.cursor.set(ListCursor {
|
||||||
|
mode: ListCursorMode::Insert,
|
||||||
|
idx: Some(idx+1)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
TerminalEditorResult::Continue => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
TerminalEditorResult::Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TerminalEditorResult::Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<ItemEditor, FnMakeItemEditor> ListEditor<ItemEditor, FnMakeItemEditor>
|
||||||
|
where ItemEditor: TerminalTreeEditor + ?Sized + Send + Sync + 'static,
|
||||||
|
FnMakeItemEditor: Fn() -> Arc<RwLock<ItemEditor>>
|
||||||
|
{
|
||||||
|
pub fn get_seg_seq_view(&self) -> OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>> {
|
||||||
|
let segment_view_port = ViewPort::<dyn SequenceView<Item = ListEditorViewSegment>>::new();
|
||||||
|
ListEditorView::new(
|
||||||
|
self.cursor_port.outer(),
|
||||||
|
self.data_port.outer().to_sequence().map(|ed| ed.read().unwrap().get_term_view()),
|
||||||
|
segment_view_port.inner()
|
||||||
|
);
|
||||||
|
|
||||||
|
segment_view_port.into_outer()
|
||||||
|
.map(
|
||||||
|
move |segment| {
|
||||||
|
let cursor_col = (90, 60, 200);
|
||||||
|
match segment {
|
||||||
|
ListEditorViewSegment::InsertCursor =>
|
||||||
|
make_label("|")
|
||||||
|
.map_item(
|
||||||
|
move |_pt, atom|
|
||||||
|
atom.add_style_back(TerminalStyle::fg_color(cursor_col))
|
||||||
|
.add_style_back(TerminalStyle::bold(true))
|
||||||
|
),
|
||||||
|
ListEditorViewSegment::Select(sub_view) =>
|
||||||
|
sub_view.map_item(
|
||||||
|
move |_pt, atom| {
|
||||||
|
let old_col = atom.style.bg_color.unwrap_or(cursor_col);
|
||||||
|
atom.add_style_front(TerminalStyle::bg_color(((old_col.0 as f32 * 0.4) as u8, (old_col.1 as f32 * 0.4) as u8, (old_col.2 as f32 * 0.4) as u8)))
|
||||||
|
}
|
||||||
|
),
|
||||||
|
ListEditorViewSegment::Modify(sub_view) => {
|
||||||
|
sub_view.clone().map_item(
|
||||||
|
move |_pt, atom| {
|
||||||
|
let old_col = atom.style.bg_color.unwrap_or(cursor_col);
|
||||||
|
atom.add_style_front(TerminalStyle::bg_color(((old_col.0 as f32 * 0.4) as u8, (old_col.1 as f32 * 0.4) as u8, (old_col.2 as f32 * 0.4) as u8)))
|
||||||
|
}
|
||||||
|
//.add_style_back(TerminalStyle::bold(true))
|
||||||
|
)
|
||||||
|
},
|
||||||
|
ListEditorViewSegment::View(sub_view) =>
|
||||||
|
sub_view.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn horizontal_sexpr_view(&self) -> OuterViewPort<dyn TerminalView> {
|
||||||
|
self.get_seg_seq_view().horizontal_sexpr_view(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vertical_sexpr_view(&self) -> OuterViewPort<dyn TerminalView> {
|
||||||
|
self.get_seg_seq_view().vertical_sexpr_view(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn path_view(&self) -> OuterViewPort<dyn TerminalView> {
|
||||||
|
self.get_seg_seq_view()
|
||||||
|
.decorate("<", ">", "/", 0)
|
||||||
|
.to_grid_horizontal()
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn string_view(&self) -> OuterViewPort<dyn TerminalView> {
|
||||||
|
self.get_seg_seq_view()
|
||||||
|
.decorate("\"", "\"", "", 1)
|
||||||
|
.to_grid_horizontal()
|
||||||
|
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clist_view(&self) -> OuterViewPort<dyn TerminalView> {
|
||||||
|
self.get_seg_seq_view()
|
||||||
|
.decorate("{", "}", ", ", 1)
|
||||||
|
.to_grid_horizontal()
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hex_view(&self) -> OuterViewPort<dyn TerminalView> {
|
||||||
|
self.get_seg_seq_view()
|
||||||
|
.decorate("0x", "", "", 0)
|
||||||
|
.to_grid_horizontal()
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn plain_view(&self) -> OuterViewPort<dyn TerminalView> {
|
||||||
|
self.get_seg_seq_view()
|
||||||
|
.to_grid_horizontal()
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(make_item_editor: FnMakeItemEditor, style: ListEditorStyle) -> Self {
|
||||||
|
let cursor_port = ViewPort::new();
|
||||||
|
let data_port = ViewPort::new();
|
||||||
|
let mut cursor = SingletonBuffer::new(ListCursor::default(), cursor_port.inner());
|
||||||
|
let mut data = VecBuffer::<Arc<RwLock<ItemEditor>>>::new(data_port.inner());
|
||||||
|
|
||||||
|
let mut le = ListEditor {
|
||||||
|
data,
|
||||||
|
data_port,
|
||||||
|
cursor,
|
||||||
|
cursor_port,
|
||||||
|
|
||||||
|
style,
|
||||||
|
make_item_editor,
|
||||||
|
level: 0,
|
||||||
|
cur_dist: Arc::new(RwLock::new(0))
|
||||||
|
};
|
||||||
|
le.set_style(style);
|
||||||
|
le
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_style(&mut self, style: ListEditorStyle) {
|
||||||
|
self.style = style;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_data_port(&self) -> OuterViewPort<dyn SequenceView<Item = Arc<RwLock<ItemEditor>>>> {
|
||||||
|
self.data_port.outer().to_sequence()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_list_cursor_port(&self) -> OuterViewPort<dyn SingletonView<Item = ListCursor>> {
|
||||||
|
self.cursor_port.outer()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_item(&self) -> Option<Arc<RwLock<ItemEditor>>> {
|
||||||
|
if let Some(idx) = self.cursor.get().idx {
|
||||||
|
if idx < self.data.len() {
|
||||||
|
Some(self.data.get(idx))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_idx(&mut self, idx: isize) {
|
||||||
|
let cur = self.cursor.get();
|
||||||
|
let mode = cur.mode;
|
||||||
|
|
||||||
|
if idx < 0 {
|
||||||
|
self.cursor.set(ListCursor {
|
||||||
|
mode,
|
||||||
|
idx: Some((self.data.len() as isize + idx) as usize)
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
self.cursor.set(ListCursor {
|
||||||
|
mode,
|
||||||
|
idx: Some(idx as usize)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_mode(&mut self, mode: ListCursorMode) {
|
||||||
|
let mut cur = self.cursor.get();
|
||||||
|
|
||||||
|
if cur.mode == ListCursorMode::Insert &&
|
||||||
|
mode != ListCursorMode::Insert
|
||||||
|
{
|
||||||
|
if let Some(idx) = cur.idx {
|
||||||
|
if idx == self.data.len() && idx > 0 {
|
||||||
|
cur.idx = Some(idx-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cur.mode = mode;
|
||||||
|
|
||||||
|
self.cursor.set(cur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<ItemEditor, FnMakeItemEditor> ListEditor<ItemEditor, FnMakeItemEditor>
|
||||||
|
where ItemEditor: TerminalTreeEditor + ?Sized + Send + Sync + 'static,
|
||||||
|
FnMakeItemEditor: Fn() -> Arc<RwLock<ItemEditor>>
|
||||||
|
{
|
||||||
|
fn set_leaf_mode(&mut self, mode: ListCursorMode) {
|
||||||
|
let mut c = self.get_cursor();
|
||||||
|
c.leaf_mode = mode;
|
||||||
|
self.goto(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
142
nested/src/list/editor_view.rs
Normal file
142
nested/src/list/editor_view.rs
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
use {
|
||||||
|
std::sync::Arc,
|
||||||
|
std::sync::RwLock,
|
||||||
|
crate::{
|
||||||
|
core::{
|
||||||
|
View,
|
||||||
|
InnerViewPort,
|
||||||
|
OuterViewPort,
|
||||||
|
Observer,
|
||||||
|
ObserverExt,
|
||||||
|
ObserverBroadcast
|
||||||
|
},
|
||||||
|
projection::ProjectionHelper,
|
||||||
|
|
||||||
|
singleton::SingletonView,
|
||||||
|
sequence::SequenceView,
|
||||||
|
terminal::TerminalView,
|
||||||
|
|
||||||
|
list::{ListCursor, ListCursorMode}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub enum ListEditorViewSegment {
|
||||||
|
InsertCursor,
|
||||||
|
View(OuterViewPort<dyn TerminalView>),
|
||||||
|
Select(OuterViewPort<dyn TerminalView>),
|
||||||
|
Modify(OuterViewPort<dyn TerminalView>)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ListEditorView {
|
||||||
|
cursor: Arc<dyn SingletonView<Item = ListCursor>>,
|
||||||
|
data: Arc<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>>,
|
||||||
|
cur_cursor: ListCursor,
|
||||||
|
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = ListEditorViewSegment>>>>,
|
||||||
|
proj_helper: ProjectionHelper<usize, Self>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl View for ListEditorView {
|
||||||
|
type Msg = usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SequenceView for ListEditorView {
|
||||||
|
type Item = ListEditorViewSegment;
|
||||||
|
|
||||||
|
fn len(&self) -> Option<usize> {
|
||||||
|
match self.cursor.get().mode {
|
||||||
|
ListCursorMode::Insert => Some(self.data.len()? + if self.cur_cursor.idx.is_some() { 1 } else { 0 }),
|
||||||
|
_ => self.data.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, idx: &usize) -> Option<Self::Item> {
|
||||||
|
Some(
|
||||||
|
if let Some(cur) = self.cur_cursor.idx {
|
||||||
|
match self.cur_cursor.mode {
|
||||||
|
ListCursorMode::Select => {
|
||||||
|
if *idx == cur {
|
||||||
|
ListEditorViewSegment::Select(self.data.get(&idx)?)
|
||||||
|
} else {
|
||||||
|
ListEditorViewSegment::View(self.data.get(&idx)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ListCursorMode::Insert => {
|
||||||
|
if *idx < cur {
|
||||||
|
ListEditorViewSegment::View(self.data.get(&idx)?)
|
||||||
|
} else if *idx == cur {
|
||||||
|
ListEditorViewSegment::InsertCursor
|
||||||
|
} else {
|
||||||
|
ListEditorViewSegment::View(self.data.get(&(idx-1))?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ListCursorMode::Modify => {
|
||||||
|
if *idx == cur {
|
||||||
|
ListEditorViewSegment::Modify(self.data.get(&idx)?)
|
||||||
|
} else {
|
||||||
|
ListEditorViewSegment::View(self.data.get(&idx)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ListEditorViewSegment::View(self.data.get(&idx)?)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ListEditorView {
|
||||||
|
pub fn new(
|
||||||
|
cursor_port: OuterViewPort<dyn SingletonView<Item = ListCursor>>,
|
||||||
|
data_port: OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>>,
|
||||||
|
out_port: InnerViewPort<dyn SequenceView<Item = ListEditorViewSegment>>
|
||||||
|
) -> Arc<RwLock<Self>> {
|
||||||
|
let mut proj_helper = ProjectionHelper::new(out_port.0.update_hooks.clone());
|
||||||
|
let proj = Arc::new(RwLock::new(
|
||||||
|
ListEditorView {
|
||||||
|
cur_cursor: ListCursor::default(),
|
||||||
|
cursor: proj_helper.new_singleton_arg(
|
||||||
|
0,
|
||||||
|
cursor_port,
|
||||||
|
|s: &mut Self, _msg| {
|
||||||
|
let old_cursor = s.cur_cursor;
|
||||||
|
let new_cursor = s.cursor.get();
|
||||||
|
s.cur_cursor = new_cursor;
|
||||||
|
|
||||||
|
s.cast.notify_each(
|
||||||
|
0 ..= s.data.len().unwrap_or(0)+1
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
data: proj_helper.new_sequence_arg(
|
||||||
|
1,
|
||||||
|
data_port,
|
||||||
|
|s: &mut Self, idx| {
|
||||||
|
if let Some(cur_idx) = s.cur_cursor.idx {
|
||||||
|
match s.cur_cursor.mode {
|
||||||
|
ListCursorMode::Insert => {
|
||||||
|
if *idx < cur_idx {
|
||||||
|
s.cast.notify(idx);
|
||||||
|
} else {
|
||||||
|
s.cast.notify(&(*idx + 1));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
s.cast.notify(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.cast.notify(idx);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
cast: out_port.get_broadcast(),
|
||||||
|
proj_helper
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
proj.write().unwrap().proj_helper.set_proj(&proj);
|
||||||
|
out_port.set_view(Some(proj.clone()));
|
||||||
|
|
||||||
|
proj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
10
nested/src/list/mod.rs
Normal file
10
nested/src/list/mod.rs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
pub mod sexpr;
|
||||||
|
pub mod cursor;
|
||||||
|
pub mod editor;
|
||||||
|
pub mod editor_view;
|
||||||
|
|
||||||
|
pub use sexpr::{SExprView, ListDecoration};
|
||||||
|
pub use cursor::{ListCursorMode, ListCursor};
|
||||||
|
pub use editor::{ListEditor, ListEditorStyle};
|
||||||
|
|
270
nested/src/list/sexpr.rs
Normal file
270
nested/src/list/sexpr.rs
Normal file
|
@ -0,0 +1,270 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
core::{InnerViewPort, Observer, ObserverBroadcast, OuterViewPort, View, ViewPort},
|
||||||
|
projection::ProjectionHelper,
|
||||||
|
sequence::SequenceView,
|
||||||
|
index::{IndexArea},
|
||||||
|
terminal::{make_label, TerminalStyle, TerminalView},
|
||||||
|
},
|
||||||
|
cgmath::Point2,
|
||||||
|
std::sync::Arc,
|
||||||
|
std::sync::RwLock,
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub struct ListDecorator {
|
||||||
|
opening_port: OuterViewPort<dyn TerminalView>,
|
||||||
|
closing_port: OuterViewPort<dyn TerminalView>,
|
||||||
|
delim_port: OuterViewPort<dyn TerminalView>,
|
||||||
|
items: Arc<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>>,
|
||||||
|
|
||||||
|
list_style: TerminalStyle,
|
||||||
|
item_style: TerminalStyle,
|
||||||
|
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>>>>,
|
||||||
|
proj_helper: ProjectionHelper<(), Self>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl View for ListDecorator {
|
||||||
|
type Msg = usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SequenceView for ListDecorator {
|
||||||
|
type Item = OuterViewPort<dyn TerminalView>;
|
||||||
|
|
||||||
|
fn len(&self) -> Option<usize> {
|
||||||
|
let l = self.items.len()?;
|
||||||
|
Some(if l == 0 { 2 } else { 2 * l + 1 })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, idx: &usize) -> Option<Self::Item> {
|
||||||
|
let item_idx = idx / 2;
|
||||||
|
let list_style = self.list_style.clone();
|
||||||
|
let item_style = self.item_style.clone();
|
||||||
|
let l = self.items.len().unwrap_or(0);
|
||||||
|
Some(if *idx == 0 {
|
||||||
|
self.opening_port
|
||||||
|
.clone()
|
||||||
|
.map_item(move |_, atom| atom.add_style_back(list_style))
|
||||||
|
} else if (l == 0 && *idx == 1) || *idx == 2 * l {
|
||||||
|
self.closing_port
|
||||||
|
.clone()
|
||||||
|
.map_item(move |_, atom| atom.add_style_back(list_style))
|
||||||
|
} else if idx % 2 == 0 {
|
||||||
|
self.delim_port
|
||||||
|
.clone()
|
||||||
|
.map_item(move |_, atom| atom.add_style_back(list_style))
|
||||||
|
} else {
|
||||||
|
self.items
|
||||||
|
.get(&item_idx)?
|
||||||
|
.map_item(move |_, atom| atom.add_style_back(item_style))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ListDecorator {
|
||||||
|
pub fn new(
|
||||||
|
opening: &str,
|
||||||
|
closing: &str,
|
||||||
|
delim: &str,
|
||||||
|
level: usize,
|
||||||
|
items_port: OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>>,
|
||||||
|
out_port: InnerViewPort<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>>,
|
||||||
|
) -> Arc<RwLock<Self>> {
|
||||||
|
let mut proj_helper = ProjectionHelper::new(out_port.0.update_hooks.clone());
|
||||||
|
|
||||||
|
let li = Arc::new(RwLock::new(ListDecorator {
|
||||||
|
opening_port: make_label(opening),
|
||||||
|
closing_port: make_label(closing),
|
||||||
|
delim_port: make_label(delim),
|
||||||
|
items: proj_helper.new_sequence_arg((), items_port, |s: &mut Self, item_idx| {
|
||||||
|
s.cast.notify(&(*item_idx * 2 + 1));
|
||||||
|
s.cast.notify(&(*item_idx * 2 + 2));
|
||||||
|
}),
|
||||||
|
list_style: TerminalStyle::fg_color(match level {
|
||||||
|
0 => (200, 120, 10),
|
||||||
|
1 => (120, 200, 10),
|
||||||
|
_ => (255, 255, 255),
|
||||||
|
}),
|
||||||
|
item_style: TerminalStyle::fg_color(match level {
|
||||||
|
_ => (255, 255, 255),
|
||||||
|
}),
|
||||||
|
cast: out_port.get_broadcast(),
|
||||||
|
proj_helper,
|
||||||
|
}));
|
||||||
|
|
||||||
|
li.write().unwrap().proj_helper.set_proj(&li);
|
||||||
|
|
||||||
|
out_port.set_view(Some(li.clone()));
|
||||||
|
li
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ListDecoration {
|
||||||
|
fn decorate(
|
||||||
|
&self,
|
||||||
|
opening: &str,
|
||||||
|
closing: &str,
|
||||||
|
delim: &str,
|
||||||
|
level: usize,
|
||||||
|
) -> OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ListDecoration for OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>> {
|
||||||
|
fn decorate(
|
||||||
|
&self,
|
||||||
|
opening: &str,
|
||||||
|
closing: &str,
|
||||||
|
delim: &str,
|
||||||
|
level: usize,
|
||||||
|
) -> OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>> {
|
||||||
|
let port = ViewPort::new();
|
||||||
|
ListDecorator::new(opening, closing, delim, level, self.clone(), port.inner());
|
||||||
|
port.into_outer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
use crate::{grid::GridView, index::IndexView};
|
||||||
|
|
||||||
|
pub struct VerticalSexprDecorator {
|
||||||
|
opening_port: OuterViewPort<dyn TerminalView>,
|
||||||
|
closing_port: OuterViewPort<dyn TerminalView>,
|
||||||
|
items: Arc<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>>,
|
||||||
|
|
||||||
|
list_style: TerminalStyle,
|
||||||
|
item_style: TerminalStyle,
|
||||||
|
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn GridView<Item = OuterViewPort<dyn TerminalView>>>>>,
|
||||||
|
proj_helper: ProjectionHelper<(), Self>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl View for VerticalSexprDecorator {
|
||||||
|
type Msg = IndexArea<Point2<i16>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndexView<Point2<i16>> for VerticalSexprDecorator {
|
||||||
|
type Item = OuterViewPort<dyn TerminalView>;
|
||||||
|
|
||||||
|
fn area(&self) -> IndexArea<Point2<i16>> {
|
||||||
|
IndexArea::Range(
|
||||||
|
Point2::new(0, 0) ..= Point2::new(2, std::cmp::max(self.items.len().unwrap() as i16 - 1, 0))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, pt: &Point2<i16>) -> Option<Self::Item> {
|
||||||
|
if pt.y < 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let item_idx = pt.y as usize;
|
||||||
|
let list_style = self.list_style.clone();
|
||||||
|
let item_style = self.item_style.clone();
|
||||||
|
let l = self.items.len().unwrap_or(0);
|
||||||
|
|
||||||
|
match pt.x {
|
||||||
|
0 => {
|
||||||
|
if pt.y == 0 {
|
||||||
|
Some(
|
||||||
|
self.opening_port
|
||||||
|
.clone()
|
||||||
|
.map_item(move |_, atom| atom.add_style_back(list_style)),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
if item_idx < l {
|
||||||
|
Some(
|
||||||
|
self.items
|
||||||
|
.get(&item_idx)?
|
||||||
|
.map_item(move |_, atom| atom.add_style_back(item_style)),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
if (l == 0 && pt.y == 0) || (item_idx + 1 == l) {
|
||||||
|
Some(
|
||||||
|
self.closing_port
|
||||||
|
.clone()
|
||||||
|
.map_item(move |_, atom| atom.add_style_back(list_style)),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VerticalSexprDecorator {
|
||||||
|
pub fn new(
|
||||||
|
opening: &str,
|
||||||
|
closing: &str,
|
||||||
|
level: usize,
|
||||||
|
items_port: OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>>,
|
||||||
|
out_port: InnerViewPort<dyn GridView<Item = OuterViewPort<dyn TerminalView>>>,
|
||||||
|
) -> Arc<RwLock<Self>> {
|
||||||
|
let mut proj_helper = ProjectionHelper::new(out_port.0.update_hooks.clone());
|
||||||
|
|
||||||
|
let li = Arc::new(RwLock::new(VerticalSexprDecorator {
|
||||||
|
opening_port: make_label(opening),
|
||||||
|
closing_port: make_label(closing),
|
||||||
|
items: proj_helper.new_sequence_arg((), items_port, |s: &mut Self, item_idx| {
|
||||||
|
s.cast.notify(
|
||||||
|
&IndexArea::Range(
|
||||||
|
Point2::new(0, *item_idx as i16) ..= Point2::new(2, *item_idx as i16)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
list_style: TerminalStyle::fg_color(match level {
|
||||||
|
0 => (200, 120, 10),
|
||||||
|
1 => (120, 200, 10),
|
||||||
|
_ => (255, 255, 255),
|
||||||
|
}),
|
||||||
|
item_style: TerminalStyle::fg_color(match level {
|
||||||
|
_ => (255, 255, 255),
|
||||||
|
}),
|
||||||
|
cast: out_port.get_broadcast(),
|
||||||
|
proj_helper,
|
||||||
|
}));
|
||||||
|
|
||||||
|
li.write().unwrap().proj_helper.set_proj(&li);
|
||||||
|
|
||||||
|
out_port.set_view(Some(li.clone()));
|
||||||
|
li
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait SExprView {
|
||||||
|
fn horizontal_sexpr_view(&self, level: usize) -> OuterViewPort<dyn TerminalView>;
|
||||||
|
fn vertical_bar_view(&self, level: usize) -> OuterViewPort<dyn TerminalView>;
|
||||||
|
fn vertical_sexpr_view(&self, level: usize) -> OuterViewPort<dyn TerminalView>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SExprView for OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn TerminalView>>> {
|
||||||
|
fn horizontal_sexpr_view(&self, level: usize) -> OuterViewPort<dyn TerminalView> {
|
||||||
|
let port = ViewPort::new();
|
||||||
|
ListDecorator::new("(", ")", " ", level, self.clone(), port.inner());
|
||||||
|
|
||||||
|
port.into_outer().to_grid_horizontal().flatten()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vertical_bar_view(&self, level: usize) -> OuterViewPort<dyn TerminalView> {
|
||||||
|
let port = ViewPort::new();
|
||||||
|
ListDecorator::new("Λ", "V", "|", level, self.clone(), port.inner());
|
||||||
|
|
||||||
|
port.into_outer().to_grid_vertical().flatten()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vertical_sexpr_view(&self, level: usize) -> OuterViewPort<dyn TerminalView> {
|
||||||
|
let port = ViewPort::new();
|
||||||
|
VerticalSexprDecorator::new("(", ")", level, self.clone(), port.inner());
|
||||||
|
port.into_outer().flatten()
|
||||||
|
}
|
||||||
|
}
|
242
nested/src/projection.rs
Normal file
242
nested/src/projection.rs
Normal file
|
@ -0,0 +1,242 @@
|
||||||
|
use {
|
||||||
|
std::{
|
||||||
|
cmp::{max},
|
||||||
|
any::Any,
|
||||||
|
sync::{Arc, Weak},
|
||||||
|
hash::Hash,
|
||||||
|
collections::HashMap
|
||||||
|
},
|
||||||
|
std::sync::RwLock,
|
||||||
|
crate::{
|
||||||
|
core::{
|
||||||
|
View,
|
||||||
|
Observer, ObserverExt,
|
||||||
|
port::UpdateTask,
|
||||||
|
OuterViewPort,
|
||||||
|
channel::{
|
||||||
|
ChannelSender, ChannelReceiver,
|
||||||
|
ChannelData,
|
||||||
|
set_channel,
|
||||||
|
queue_channel
|
||||||
|
}
|
||||||
|
},
|
||||||
|
singleton::{SingletonView},
|
||||||
|
sequence::{SequenceView},
|
||||||
|
index::{IndexArea, IndexView}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub struct ProjectionHelper<ArgKey, P>
|
||||||
|
where ArgKey: Clone + Hash + Eq,
|
||||||
|
P: Send + Sync + 'static
|
||||||
|
{
|
||||||
|
keepalive: HashMap<ArgKey, (usize, Arc<dyn Any + Send + Sync>)>,
|
||||||
|
proj: Arc<RwLock<Weak<RwLock<P>>>>,
|
||||||
|
update_hooks: Arc<RwLock<Vec<Arc<dyn UpdateTask>>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<ArgKey, P> ProjectionHelper<ArgKey, P>
|
||||||
|
where ArgKey: Clone + Hash + Eq,
|
||||||
|
P: Send + Sync + 'static
|
||||||
|
{
|
||||||
|
pub fn new(update_hooks: Arc<RwLock<Vec<Arc<dyn UpdateTask>>>>) -> Self {
|
||||||
|
ProjectionHelper {
|
||||||
|
keepalive: HashMap::new(),
|
||||||
|
proj: Arc::new(RwLock::new(Weak::new())),
|
||||||
|
update_hooks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_proj(&mut self, proj: &Arc<RwLock<P>>) {
|
||||||
|
*self.proj.write().unwrap() = Arc::downgrade(proj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: make this functions generic over the View
|
||||||
|
// this does currently not work because Observer<V> is not implemented for ProjectionArg for *all* V.
|
||||||
|
|
||||||
|
pub fn new_singleton_arg<Item: 'static>(
|
||||||
|
&mut self,
|
||||||
|
arg_key: ArgKey,
|
||||||
|
port: OuterViewPort<dyn SingletonView<Item = Item>>,
|
||||||
|
notify: impl Fn(&mut P, &()) + Send + Sync + 'static
|
||||||
|
) -> Arc<RwLock<Option<Arc<dyn SingletonView<Item = Item>>>>> {
|
||||||
|
port.add_observer(self.new_arg(arg_key, Arc::new(port.0.clone()), notify, set_channel()));
|
||||||
|
port.get_view_arc()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_sequence_arg<Item: 'static>(
|
||||||
|
&mut self,
|
||||||
|
arg_key: ArgKey,
|
||||||
|
port: OuterViewPort<dyn SequenceView<Item = Item>>,
|
||||||
|
notify: impl Fn(&mut P, &usize) + Send + Sync + 'static
|
||||||
|
) -> Arc<RwLock<Option<Arc<dyn SequenceView<Item = Item>>>>> {
|
||||||
|
port.add_observer(self.new_arg(arg_key, Arc::new(port.0.clone()), notify, set_channel()));
|
||||||
|
port.get_view_arc()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_index_arg<Key: Clone + Send + Sync + 'static, Item: 'static>(
|
||||||
|
&mut self,
|
||||||
|
arg_key: ArgKey,
|
||||||
|
port: OuterViewPort<dyn IndexView<Key, Item = Item>>,
|
||||||
|
notify: impl Fn(&mut P, &IndexArea<Key>) + Send + Sync + 'static
|
||||||
|
) -> Arc<RwLock<Option<Arc<dyn IndexView<Key, Item = Item>>>>> {
|
||||||
|
port.add_observer(self.new_arg(arg_key, Arc::new(port.0.clone()), notify, queue_channel()));
|
||||||
|
port.get_view_arc()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_arg<
|
||||||
|
V: View + ?Sized + 'static,
|
||||||
|
D: ChannelData<Item = V::Msg> + 'static
|
||||||
|
>(
|
||||||
|
&mut self,
|
||||||
|
arg_key: ArgKey,
|
||||||
|
src_update: Arc<dyn UpdateTask>,
|
||||||
|
notify: impl Fn(&mut P, &V::Msg) + Send + Sync + 'static,
|
||||||
|
(tx, rx): (ChannelSender<D>, ChannelReceiver<D>)
|
||||||
|
)
|
||||||
|
-> Arc<RwLock<ProjectionArg<P, V, D>>>
|
||||||
|
where V::Msg: Send + Sync,
|
||||||
|
D::IntoIter: Send + Sync + 'static
|
||||||
|
{
|
||||||
|
self.remove_arg(&arg_key);
|
||||||
|
|
||||||
|
let arg = Arc::new(RwLock::new(
|
||||||
|
ProjectionArg {
|
||||||
|
src: None,
|
||||||
|
notify: Box::new(notify),
|
||||||
|
proj: self.proj.clone(),
|
||||||
|
rx, tx
|
||||||
|
}));
|
||||||
|
|
||||||
|
let mut hooks = self.update_hooks.write().unwrap();
|
||||||
|
let idx = hooks.len();
|
||||||
|
hooks.push(src_update);
|
||||||
|
hooks.push(arg.clone());
|
||||||
|
self.keepalive.insert(arg_key, (idx, arg.clone()));
|
||||||
|
|
||||||
|
arg
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_arg(&mut self, arg_key: &ArgKey) {
|
||||||
|
let mut hooks = self.update_hooks.write().unwrap();
|
||||||
|
if let Some((idx, arg)) = self.keepalive.remove(arg_key) {
|
||||||
|
hooks.remove(idx);
|
||||||
|
hooks.remove(idx);
|
||||||
|
for (_, (j, _)) in self.keepalive.iter_mut() {
|
||||||
|
if *j > idx {
|
||||||
|
*j -= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Special Observer which can access the state of the projection on notify
|
||||||
|
/// also handles the reset()
|
||||||
|
pub struct ProjectionArg<P, V, D>
|
||||||
|
where P: Send + Sync + 'static,
|
||||||
|
V: View + ?Sized,
|
||||||
|
D: ChannelData<Item = V::Msg>,
|
||||||
|
D::IntoIter: Send + Sync
|
||||||
|
{
|
||||||
|
src: Option<Arc<V>>,
|
||||||
|
notify: Box<dyn Fn(&mut P, &V::Msg) + Send + Sync + 'static>,
|
||||||
|
proj: Arc<RwLock<Weak<RwLock<P>>>>,
|
||||||
|
rx: ChannelReceiver<D>,
|
||||||
|
tx: ChannelSender<D>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P, V, D> UpdateTask for ProjectionArg<P, V, D>
|
||||||
|
where P: Send + Sync + 'static,
|
||||||
|
V: View + ?Sized,
|
||||||
|
D: ChannelData<Item = V::Msg>,
|
||||||
|
D::IntoIter: Send + Sync
|
||||||
|
{
|
||||||
|
fn update(&self) {
|
||||||
|
if let Some(p) = self.proj.read().unwrap().upgrade() {
|
||||||
|
if let Some(data) = self.rx.try_recv() {
|
||||||
|
for msg in data {
|
||||||
|
//eprintln!("proj update {:?}", msg);
|
||||||
|
(self.notify)(
|
||||||
|
&mut *p.write().unwrap(),
|
||||||
|
&msg
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
eprintln!("proj update: upgrade fail");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P, V, D> UpdateTask for RwLock<ProjectionArg<P, V, D>>
|
||||||
|
where P: Send + Sync + 'static,
|
||||||
|
V: View + ?Sized,
|
||||||
|
D: ChannelData<Item = V::Msg>,
|
||||||
|
D::IntoIter: Send + Sync
|
||||||
|
{
|
||||||
|
fn update(&self) {
|
||||||
|
self.read().unwrap().update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<P, Item, D> Observer<dyn SingletonView<Item = Item>> for ProjectionArg<P, dyn SingletonView<Item = Item>, D>
|
||||||
|
where P: Send + Sync + 'static,
|
||||||
|
D: ChannelData<Item = ()>,
|
||||||
|
D::IntoIter: Send + Sync
|
||||||
|
{
|
||||||
|
fn reset(&mut self, new_src: Option<Arc<dyn SingletonView<Item = Item>>>) {
|
||||||
|
self.src = new_src;
|
||||||
|
self.notify(&());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&mut self, msg: &()) {
|
||||||
|
self.tx.send(msg.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<P, Item, D> Observer<dyn SequenceView<Item = Item>> for ProjectionArg<P, dyn SequenceView<Item = Item>, D>
|
||||||
|
where P: Send + Sync + 'static,
|
||||||
|
D: ChannelData<Item = usize>,
|
||||||
|
D::IntoIter: Send + Sync
|
||||||
|
{
|
||||||
|
fn reset(&mut self, new_src: Option<Arc<dyn SequenceView<Item = Item>>>) {
|
||||||
|
let old_len = self.src.len().unwrap_or(0);
|
||||||
|
self.src = new_src;
|
||||||
|
let new_len = self.src.len().unwrap_or(0);
|
||||||
|
|
||||||
|
self.notify_each(0 .. max(old_len, new_len));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&mut self, msg: &usize) {
|
||||||
|
self.tx.send(*msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<P, Key, Item, D> Observer<dyn IndexView<Key, Item = Item>> for ProjectionArg<P, dyn IndexView<Key, Item = Item>, D>
|
||||||
|
where P: Send + Sync + 'static,
|
||||||
|
Key: Clone + Send + Sync,
|
||||||
|
D: ChannelData<Item = IndexArea<Key>>,
|
||||||
|
D::IntoIter: Send + Sync
|
||||||
|
{
|
||||||
|
fn reset(&mut self, new_src: Option<Arc<dyn IndexView<Key, Item = Item>>>) {
|
||||||
|
let old_area = self.src.area();
|
||||||
|
self.src = new_src;
|
||||||
|
|
||||||
|
self.notify(&old_area);
|
||||||
|
self.notify(&self.src.area())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&mut self, msg: &IndexArea<Key>) {
|
||||||
|
self.tx.send(msg.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
4
nested/src/sdf/mod.rs
Normal file
4
nested/src/sdf/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
pub mod term;
|
||||||
|
|
||||||
|
pub use term::SdfTerm;
|
||||||
|
|
202
nested/src/sdf/term.rs
Normal file
202
nested/src/sdf/term.rs
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
|
||||||
|
use {
|
||||||
|
std::{
|
||||||
|
sync::{Arc, RwLock, Mutex},
|
||||||
|
collections::HashMap
|
||||||
|
},
|
||||||
|
cgmath::{Point2, Vector2},
|
||||||
|
termion::event::{Event, Key},
|
||||||
|
crate::{
|
||||||
|
core::{
|
||||||
|
View,
|
||||||
|
ViewPort,
|
||||||
|
Observer,
|
||||||
|
ObserverExt,
|
||||||
|
OuterViewPort,
|
||||||
|
port::UpdateTask
|
||||||
|
},
|
||||||
|
index::{IndexArea},
|
||||||
|
terminal::{TerminalView}
|
||||||
|
},
|
||||||
|
nako::{
|
||||||
|
stream::{SecondaryStream2d, PrimaryStream2d},
|
||||||
|
glam::{Vec2, Vec3, UVec2, IVec2},
|
||||||
|
operations::{
|
||||||
|
planar::primitives2d::Box2d,
|
||||||
|
volumetric::{Color, Union, Round},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nakorender::{
|
||||||
|
backend::{Backend, LayerId, LayerId2d, LayerInfo, LayerSampling},
|
||||||
|
marp::MarpBackend,
|
||||||
|
winit, camera::Camera2d
|
||||||
|
},
|
||||||
|
nako_std::{
|
||||||
|
text::Character
|
||||||
|
},
|
||||||
|
std::{fs::File, io::Read, mem::needs_drop, path::Path},
|
||||||
|
font_kit::font::Font,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct SdfTerm {
|
||||||
|
src_view: Option<Arc<dyn TerminalView>>,
|
||||||
|
bg_layers: HashMap<Point2<i16>, (bool, LayerId2d)>,
|
||||||
|
fg_layers: HashMap<Point2<i16>, (bool, LayerId2d)>,
|
||||||
|
|
||||||
|
font_height: u32,
|
||||||
|
font: Arc<Vec<u8>>,
|
||||||
|
|
||||||
|
renderer: Arc<Mutex<MarpBackend>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SdfTerm {
|
||||||
|
pub fn new(renderer: Arc<Mutex<MarpBackend>>) -> Self {
|
||||||
|
|
||||||
|
let font_path = Path::new("/usr/share/fonts/TTF/FiraCode-Medium.ttf");
|
||||||
|
let mut font_file = File::open(font_path).unwrap();
|
||||||
|
let mut font_data = Vec::new();
|
||||||
|
font_file.read_to_end(&mut font_data).unwrap();
|
||||||
|
|
||||||
|
SdfTerm {
|
||||||
|
src_view: None,
|
||||||
|
bg_layers: HashMap::new(),
|
||||||
|
fg_layers: HashMap::new(),
|
||||||
|
font_height: 30,
|
||||||
|
font: Arc::new(font_data),
|
||||||
|
renderer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_order(&self) -> Vec<LayerId> {
|
||||||
|
vec![
|
||||||
|
self.bg_layers.iter(),
|
||||||
|
self.fg_layers.iter()
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.filter_map(
|
||||||
|
|(_pt,(active,id))|
|
||||||
|
if *active {
|
||||||
|
Some((*id).into())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, pt: &Point2<i16>) {
|
||||||
|
if self.bg_layers.get(pt).is_none() {
|
||||||
|
let id = self.renderer.lock().unwrap().new_layer_2d();
|
||||||
|
|
||||||
|
self.renderer.lock().unwrap().update_camera_2d(
|
||||||
|
id.into(),
|
||||||
|
Camera2d {
|
||||||
|
extent: Vec2::new(0.5, 1.0),
|
||||||
|
location: Vec2::new(0.0, 0.0),
|
||||||
|
rotation: 0.0
|
||||||
|
});
|
||||||
|
self.renderer.lock().unwrap().set_layer_info(
|
||||||
|
id.into(),
|
||||||
|
LayerInfo {
|
||||||
|
extent: UVec2::new(1 + self.font_height / 2, self.font_height),
|
||||||
|
location: IVec2::new(pt.x as i32 * self.font_height as i32 / 2, pt.y as i32 * self.font_height as i32),
|
||||||
|
sampling: LayerSampling::OverSampling(1),
|
||||||
|
});
|
||||||
|
|
||||||
|
self.bg_layers.insert(*pt, (false, id));
|
||||||
|
}
|
||||||
|
if self.fg_layers.get(pt).is_none() {
|
||||||
|
let id = self.renderer.lock().unwrap().new_layer_2d();
|
||||||
|
|
||||||
|
self.renderer.lock().unwrap().update_camera_2d(
|
||||||
|
id.into(),
|
||||||
|
Camera2d {
|
||||||
|
extent: Vec2::new(0.5, 1.0),
|
||||||
|
location: Vec2::new(0.0, 0.0),
|
||||||
|
rotation: 0.0
|
||||||
|
});
|
||||||
|
self.renderer.lock().unwrap().set_layer_info(
|
||||||
|
id.into(),
|
||||||
|
LayerInfo {
|
||||||
|
extent: UVec2::new(1 + self.font_height / 2, self.font_height),
|
||||||
|
location: IVec2::new(pt.x as i32 * self.font_height as i32 / 2, pt.y as i32 * self.font_height as i32),
|
||||||
|
sampling: LayerSampling::OverSampling(4),
|
||||||
|
});
|
||||||
|
|
||||||
|
self.fg_layers.insert(*pt, (false, id));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(atom) = self.src_view.get(pt) {
|
||||||
|
|
||||||
|
// background layer
|
||||||
|
if let Some((r,g,b)) = atom.style.bg_color {
|
||||||
|
let mut stream = PrimaryStream2d::new()
|
||||||
|
.push(
|
||||||
|
SecondaryStream2d::new(
|
||||||
|
Union,
|
||||||
|
Box2d {
|
||||||
|
extent: Vec2::new(0.6, 1.0)
|
||||||
|
}
|
||||||
|
).push_mod(
|
||||||
|
Color(
|
||||||
|
Vec3::new(
|
||||||
|
(r as f32 / 255.0).clamp(0.0, 1.0),
|
||||||
|
(g as f32 / 255.0).clamp(0.0, 1.0),
|
||||||
|
(b as f32 / 255.0).clamp(0.0, 1.0),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).build()
|
||||||
|
);
|
||||||
|
|
||||||
|
self.renderer.lock().unwrap().update_sdf_2d(self.bg_layers.get(pt).unwrap().1, stream.build());
|
||||||
|
self.bg_layers.get_mut(pt).unwrap().0 = true;
|
||||||
|
} else {
|
||||||
|
self.bg_layers.get_mut(pt).unwrap().0 = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// foreground layer
|
||||||
|
if let Some(c) = atom.c {
|
||||||
|
let font_index = 0;
|
||||||
|
let fontkit = Font::from_bytes(self.font.clone(), font_index).unwrap();
|
||||||
|
let mut ch = Character::from_font(&fontkit, c);//.with_size(1.0);
|
||||||
|
|
||||||
|
let (r,g,b) = atom.style.fg_color.unwrap_or((0, 0, 0));
|
||||||
|
|
||||||
|
let mut stream = PrimaryStream2d::new();
|
||||||
|
stream = ch.record_modifier_character(
|
||||||
|
stream,
|
||||||
|
Vec2::new(0.0, 0.0),
|
||||||
|
1.0,
|
||||||
|
Color(Vec3::new(
|
||||||
|
(r as f32 / 255.0).clamp(0.0, 1.0),
|
||||||
|
(g as f32 / 255.0).clamp(0.0, 1.0),
|
||||||
|
(b as f32 / 255.0).clamp(0.0, 1.0),
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
self.renderer.lock().unwrap().update_sdf_2d(self.fg_layers.get(pt).unwrap().1, stream.build());
|
||||||
|
self.fg_layers.get_mut(pt).unwrap().0 = true;
|
||||||
|
} else {
|
||||||
|
self.fg_layers.get_mut(pt).unwrap().0 = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
self.bg_layers.get_mut(pt).unwrap().0 = false;
|
||||||
|
self.fg_layers.get_mut(pt).unwrap().0 = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Observer<dyn TerminalView> for SdfTerm {
|
||||||
|
fn reset(&mut self, new_view: Option<Arc<dyn TerminalView>>) {
|
||||||
|
self.src_view = new_view;
|
||||||
|
self.notify(&self.src_view.area());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&mut self, area: &IndexArea<Point2<i16>>) {
|
||||||
|
for pt in area.iter() {
|
||||||
|
self.update(&pt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
156
nested/src/sequence/filter.rs
Normal file
156
nested/src/sequence/filter.rs
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
use {
|
||||||
|
std::sync::Arc,
|
||||||
|
std::sync::RwLock,
|
||||||
|
crate::{
|
||||||
|
sequence::{SequenceView},
|
||||||
|
core::{
|
||||||
|
Observer, ObserverExt, ObserverBroadcast,
|
||||||
|
View, ViewPort, OuterViewPort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<V: SequenceView + ?Sized + 'static> OuterViewPort<V>
|
||||||
|
{
|
||||||
|
pub fn filter<
|
||||||
|
P: Fn(&V::Item) -> bool + Send + Sync + 'static
|
||||||
|
>(&self, pred: P) -> OuterViewPort<dyn SequenceView<Item = V::Item>> {
|
||||||
|
let port = ViewPort::new();
|
||||||
|
port.add_update_hook(Arc::new(self.0.clone()));
|
||||||
|
|
||||||
|
let filter = Arc::new(RwLock::new(
|
||||||
|
Filter {
|
||||||
|
src_view: None,
|
||||||
|
pred,
|
||||||
|
old_preds: RwLock::new(Vec::new()),
|
||||||
|
cast: port.inner().get_broadcast()
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
self.add_observer(filter.clone());
|
||||||
|
port.inner().set_view(Some(filter));
|
||||||
|
port.into_outer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
struct Filter<SrcView, P>
|
||||||
|
where SrcView: SequenceView + ?Sized + 'static,
|
||||||
|
P: Fn(&SrcView::Item) -> bool + Send + Sync + 'static
|
||||||
|
{
|
||||||
|
src_view: Option<Arc<SrcView>>,
|
||||||
|
pred: P,
|
||||||
|
old_preds: RwLock<Vec<bool>>,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = SrcView::Item>>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SrcView, P> Filter<SrcView, P>
|
||||||
|
where SrcView: SequenceView + ?Sized + 'static,
|
||||||
|
P: Fn(&SrcView::Item) -> bool + Send + Sync + 'static
|
||||||
|
{
|
||||||
|
fn get_offset(&self, idx: usize) -> usize {
|
||||||
|
if let Some(v) = self.src_view.clone() {
|
||||||
|
let mut i = 0;
|
||||||
|
let mut j = 0;
|
||||||
|
let mut offset = 0;
|
||||||
|
|
||||||
|
while let (Some(x), true) = (v.get(&i), j <= idx) {
|
||||||
|
if (self.pred)(&x) {
|
||||||
|
j += 1;
|
||||||
|
} else {
|
||||||
|
offset += 1;
|
||||||
|
}
|
||||||
|
i+=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<SrcView, P> View for Filter<SrcView, P>
|
||||||
|
where SrcView: SequenceView + ?Sized + 'static,
|
||||||
|
P: Fn(&SrcView::Item) -> bool + Send + Sync + 'static
|
||||||
|
{
|
||||||
|
type Msg = usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SrcView, P> SequenceView for Filter<SrcView, P>
|
||||||
|
where SrcView: SequenceView + ?Sized + 'static,
|
||||||
|
P: Fn(&SrcView::Item) -> bool + Send + Sync + 'static
|
||||||
|
{
|
||||||
|
type Item = SrcView::Item;
|
||||||
|
|
||||||
|
fn len(&self) -> Option<usize> {
|
||||||
|
if let Some(src_len) = self.src_view.len() {
|
||||||
|
Some(src_len - self.get_offset(src_len) )
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, idx: &usize) -> Option<Self::Item> {
|
||||||
|
self.src_view.get(&( idx + self.get_offset(*idx) ))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<SrcView, P> Observer<SrcView> for Filter<SrcView, P>
|
||||||
|
where SrcView: SequenceView + ?Sized + 'static,
|
||||||
|
P: Fn(&SrcView::Item) -> bool + Send + Sync + 'static
|
||||||
|
{
|
||||||
|
fn reset(&mut self, new_src: Option<Arc<SrcView>>) {
|
||||||
|
let old_len = self.len();
|
||||||
|
self.src_view = new_src;
|
||||||
|
self.old_preds = RwLock::new(Vec::new());
|
||||||
|
let new_len = self.len();
|
||||||
|
|
||||||
|
if let Some(len) = old_len { self.cast.notify_each(0 .. len); }
|
||||||
|
if let Some(len) = new_len { self.cast.notify_each(0 .. len); }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&mut self, idx: &usize) {
|
||||||
|
let l = self.len().unwrap_or(0)+1;
|
||||||
|
let np =
|
||||||
|
if let Some(x) = self.src_view.get(idx) {
|
||||||
|
(self.pred)(&x)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut opds = self.old_preds.write().unwrap();
|
||||||
|
|
||||||
|
opds.resize_with(1+*idx, || false);
|
||||||
|
let op = opds.get(*idx).cloned().unwrap_or(false);
|
||||||
|
*opds.get_mut(*idx).unwrap() = np;
|
||||||
|
|
||||||
|
drop(opds);
|
||||||
|
|
||||||
|
let i =
|
||||||
|
(0 .. *idx)
|
||||||
|
.map(
|
||||||
|
|j|
|
||||||
|
if let Some(x) = self.src_view.get(&j) {
|
||||||
|
if (self.pred)(&x) { 1 } else { 0 }
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.sum();
|
||||||
|
|
||||||
|
if np != op {
|
||||||
|
self.cast.notify_each(i .. l);
|
||||||
|
} else {
|
||||||
|
self.cast.notify(&i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
182
nested/src/sequence/flatten.rs
Normal file
182
nested/src/sequence/flatten.rs
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
use {
|
||||||
|
std::{
|
||||||
|
sync::Arc,
|
||||||
|
collections::BTreeMap
|
||||||
|
},
|
||||||
|
std::sync::RwLock,
|
||||||
|
crate::{
|
||||||
|
core::{
|
||||||
|
View, Observer, ObserverBroadcast, ObserverExt,
|
||||||
|
ViewPort, InnerViewPort, OuterViewPort,
|
||||||
|
port::UpdateTask
|
||||||
|
},
|
||||||
|
sequence::SequenceView,
|
||||||
|
projection::ProjectionHelper
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
impl<Item> OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn SequenceView<Item = Item>>>>
|
||||||
|
where Item: 'static{
|
||||||
|
pub fn flatten(&self) -> OuterViewPort<dyn SequenceView<Item = Item>> {
|
||||||
|
let port = ViewPort::new();
|
||||||
|
Flatten::new(self.clone(), port.inner());
|
||||||
|
port.into_outer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Chunk<Item>
|
||||||
|
where Item: 'static
|
||||||
|
{
|
||||||
|
offset: usize,
|
||||||
|
len: usize,
|
||||||
|
view: Arc<dyn SequenceView<Item = Item>>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Flatten<Item>
|
||||||
|
where Item: 'static
|
||||||
|
{
|
||||||
|
length: usize,
|
||||||
|
top: Arc<dyn SequenceView<Item = OuterViewPort<dyn SequenceView<Item = Item>>>>,
|
||||||
|
chunks: BTreeMap<usize, Chunk<Item>>,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = Item>>>>,
|
||||||
|
proj_helper: ProjectionHelper<usize, Self>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Item> View for Flatten<Item>
|
||||||
|
where Item: 'static
|
||||||
|
{
|
||||||
|
type Msg = usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Item> SequenceView for Flatten<Item>
|
||||||
|
where Item: 'static
|
||||||
|
{
|
||||||
|
type Item = Item;
|
||||||
|
|
||||||
|
fn get(&self, idx: &usize) -> Option<Self::Item> {
|
||||||
|
let chunk = self.chunks.get(&self.get_chunk_idx(*idx)?)?;
|
||||||
|
chunk.view.get(&(*idx - chunk.offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> Option<usize> {
|
||||||
|
Some(self.length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: remove unused projection args (bot-views) if they get replaced by a new viewport */
|
||||||
|
impl<Item> Flatten<Item>
|
||||||
|
where Item: 'static
|
||||||
|
{
|
||||||
|
pub fn new(
|
||||||
|
top_port: OuterViewPort<dyn SequenceView<Item = OuterViewPort<dyn SequenceView<Item = Item>>>>,
|
||||||
|
out_port: InnerViewPort<dyn SequenceView<Item = Item>>
|
||||||
|
) -> Arc<RwLock<Self>> {
|
||||||
|
let mut proj_helper = ProjectionHelper::new(out_port.0.update_hooks.clone());
|
||||||
|
|
||||||
|
let flat = Arc::new(RwLock::new(
|
||||||
|
Flatten {
|
||||||
|
length: 0,
|
||||||
|
top: proj_helper.new_sequence_arg(
|
||||||
|
usize::MAX,
|
||||||
|
top_port,
|
||||||
|
|s: &mut Self, chunk_idx| {
|
||||||
|
s.update_chunk(*chunk_idx);
|
||||||
|
}
|
||||||
|
),
|
||||||
|
chunks: BTreeMap::new(),
|
||||||
|
cast: out_port.get_broadcast(),
|
||||||
|
proj_helper
|
||||||
|
}));
|
||||||
|
|
||||||
|
flat.write().unwrap().proj_helper.set_proj(&flat);
|
||||||
|
out_port.set_view(Some(flat.clone()));
|
||||||
|
flat
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the top-sequence has changed the item at chunk_idx,
|
||||||
|
/// create a new observer for the contained sub sequence
|
||||||
|
fn update_chunk(&mut self, chunk_idx: usize) {
|
||||||
|
if let Some(chunk_port) = self.top.get(&chunk_idx) {
|
||||||
|
self.chunks.insert(
|
||||||
|
chunk_idx,
|
||||||
|
Chunk {
|
||||||
|
offset: 0, // will be adjusted by update_offsets() later
|
||||||
|
len: 0,
|
||||||
|
view: self.proj_helper.new_sequence_arg(
|
||||||
|
chunk_idx,
|
||||||
|
chunk_port.clone(),
|
||||||
|
move |s: &mut Self, idx| {
|
||||||
|
if let Some(chunk) = s.chunks.get(&chunk_idx) {
|
||||||
|
let chunk_offset = chunk.offset;
|
||||||
|
let chunk_len = chunk.view.len().unwrap_or(0);
|
||||||
|
|
||||||
|
let mut dirty_idx = Vec::new();
|
||||||
|
if chunk.len != chunk_len {
|
||||||
|
dirty_idx = s.update_all_offsets();
|
||||||
|
}
|
||||||
|
|
||||||
|
s.cast.notify(&(idx + chunk_offset));
|
||||||
|
s.cast.notify_each(dirty_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
chunk_port.0.update();
|
||||||
|
|
||||||
|
let dirty_idx = self.update_all_offsets();
|
||||||
|
self.cast.notify_each(dirty_idx);
|
||||||
|
} else {
|
||||||
|
// todo:
|
||||||
|
self.proj_helper.remove_arg(&chunk_idx);
|
||||||
|
|
||||||
|
self.chunks.remove(&chunk_idx);
|
||||||
|
|
||||||
|
let dirty_idx = self.update_all_offsets();
|
||||||
|
self.cast.notify_each(dirty_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// recalculate all chunk offsets beginning at start_idx
|
||||||
|
/// and update length of flattened sequence
|
||||||
|
fn update_all_offsets(&mut self) -> Vec<usize> {
|
||||||
|
let mut dirty_idx = Vec::new();
|
||||||
|
let mut cur_offset = 0;
|
||||||
|
|
||||||
|
for (_chunk_idx, chunk) in self.chunks.iter_mut() {
|
||||||
|
let old_offset = chunk.offset;
|
||||||
|
chunk.offset = cur_offset;
|
||||||
|
chunk.len = chunk.view.len().unwrap_or(0);
|
||||||
|
|
||||||
|
if old_offset != cur_offset {
|
||||||
|
dirty_idx.extend(
|
||||||
|
std::cmp::min(old_offset, cur_offset)
|
||||||
|
.. std::cmp::max(old_offset, cur_offset) + chunk.len
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
cur_offset += chunk.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
let old_length = self.length;
|
||||||
|
self.length = cur_offset;
|
||||||
|
|
||||||
|
dirty_idx.extend(self.length .. old_length);
|
||||||
|
dirty_idx
|
||||||
|
}
|
||||||
|
|
||||||
|
/// given an index in the flattened sequence,
|
||||||
|
/// which sub-sequence does it belong to?
|
||||||
|
fn get_chunk_idx(&self, glob_idx: usize) -> Option<usize> {
|
||||||
|
let mut offset = 0;
|
||||||
|
for (chunk_idx, chunk) in self.chunks.iter() {
|
||||||
|
offset += chunk.view.len().unwrap_or(0);
|
||||||
|
if glob_idx < offset {
|
||||||
|
return Some(*chunk_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
104
nested/src/sequence/map.rs
Normal file
104
nested/src/sequence/map.rs
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
use {
|
||||||
|
std::sync::Arc,
|
||||||
|
std::sync::RwLock,
|
||||||
|
crate::{
|
||||||
|
sequence::{SequenceView},
|
||||||
|
core::{
|
||||||
|
Observer, ObserverExt, ObserverBroadcast,
|
||||||
|
View, ViewPort, OuterViewPort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<Item: 'static> OuterViewPort<dyn SequenceView<Item = Item>> {
|
||||||
|
pub fn map<
|
||||||
|
DstItem: 'static,
|
||||||
|
F: Fn(&Item) -> DstItem + Send + Sync + 'static
|
||||||
|
>(
|
||||||
|
&self,
|
||||||
|
f: F
|
||||||
|
) -> OuterViewPort<dyn SequenceView<Item = DstItem>> {
|
||||||
|
let port = ViewPort::new();
|
||||||
|
port.add_update_hook(Arc::new(self.0.clone()));
|
||||||
|
|
||||||
|
let map = Arc::new(RwLock::new(MapSequenceItem {
|
||||||
|
src_view: None,
|
||||||
|
f,
|
||||||
|
cast: port.inner().get_broadcast()
|
||||||
|
}));
|
||||||
|
|
||||||
|
self.add_observer(map.clone());
|
||||||
|
port.inner().set_view(Some(map));
|
||||||
|
port.into_outer()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn filter_map<
|
||||||
|
DstItem: Clone + 'static,
|
||||||
|
F: Fn(&Item) -> Option<DstItem> + Send + Sync + 'static
|
||||||
|
>(
|
||||||
|
&self,
|
||||||
|
f: F
|
||||||
|
) -> OuterViewPort<dyn SequenceView<Item = DstItem>> {
|
||||||
|
self.map(f)
|
||||||
|
.filter(|x| x.is_some())
|
||||||
|
.map(|x| x.clone().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub struct MapSequenceItem<DstItem, SrcView, F>
|
||||||
|
where SrcView: SequenceView + ?Sized,
|
||||||
|
F: Fn(&SrcView::Item) -> DstItem + Send + Sync
|
||||||
|
{
|
||||||
|
src_view: Option<Arc<SrcView>>,
|
||||||
|
f: F,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = DstItem>>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<DstItem, SrcView, F> View for MapSequenceItem<DstItem, SrcView, F>
|
||||||
|
where SrcView: SequenceView + ?Sized,
|
||||||
|
F: Fn(&SrcView::Item) -> DstItem + Send + Sync
|
||||||
|
{
|
||||||
|
type Msg = usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<DstItem, SrcView, F> SequenceView for MapSequenceItem<DstItem, SrcView, F>
|
||||||
|
where SrcView: SequenceView + ?Sized,
|
||||||
|
F: Fn(&SrcView::Item) -> DstItem + Send + Sync
|
||||||
|
{
|
||||||
|
type Item = DstItem;
|
||||||
|
|
||||||
|
fn len(&self) -> Option<usize> {
|
||||||
|
self.src_view.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, idx: &usize) -> Option<DstItem> {
|
||||||
|
self.src_view.get(idx).as_ref().map(|item| (self.f)(item))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<DstItem, SrcView, F> Observer<SrcView> for MapSequenceItem<DstItem, SrcView, F>
|
||||||
|
where SrcView: SequenceView + ?Sized,
|
||||||
|
F: Fn(&SrcView::Item) -> DstItem + Send + Sync
|
||||||
|
{
|
||||||
|
fn reset(&mut self, view: Option<Arc<SrcView>>) {
|
||||||
|
let old_len = self.len();
|
||||||
|
self.src_view = view;
|
||||||
|
let new_len = self.len();
|
||||||
|
|
||||||
|
if let Some(len) = old_len { self.cast.notify_each(0 .. len ); }
|
||||||
|
if let Some(len) = new_len { self.cast.notify_each(0 .. len ); }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&mut self, msg: &usize) {
|
||||||
|
self.cast.notify(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
101
nested/src/sequence/mod.rs
Normal file
101
nested/src/sequence/mod.rs
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
|
||||||
|
pub mod seq2idx;
|
||||||
|
pub mod map;
|
||||||
|
pub mod filter;
|
||||||
|
pub mod flatten;
|
||||||
|
|
||||||
|
pub use seq2idx::{Sequence2Index};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
use crate::core::View;
|
||||||
|
|
||||||
|
pub trait SequenceView : View<Msg = usize> {
|
||||||
|
type Item;
|
||||||
|
|
||||||
|
fn get(&self, idx: &usize) -> Option<Self::Item>;
|
||||||
|
fn len(&self) -> Option<usize>;
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub trait SequenceViewExt : SequenceView {
|
||||||
|
fn iter<'a>(&'a self) -> SequenceViewIter<'a, Self> {
|
||||||
|
SequenceViewIter {
|
||||||
|
view: self,
|
||||||
|
cur: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: SequenceView + ?Sized> SequenceViewExt for V {}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub struct SequenceViewIter<'a, V>
|
||||||
|
where V: SequenceView + ?Sized
|
||||||
|
{
|
||||||
|
view: &'a V,
|
||||||
|
cur: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, V> Iterator for SequenceViewIter<'a, V>
|
||||||
|
where V: SequenceView + ?Sized
|
||||||
|
{
|
||||||
|
type Item = V::Item;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let i = self.cur;
|
||||||
|
self.cur += 1;
|
||||||
|
self.view.get(&i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
sync::Arc,
|
||||||
|
ops::{Deref}
|
||||||
|
};
|
||||||
|
use std::sync::RwLock;
|
||||||
|
|
||||||
|
impl<V: SequenceView + ?Sized> SequenceView for RwLock<V> {
|
||||||
|
type Item = V::Item;
|
||||||
|
|
||||||
|
fn get(&self, idx: &usize) -> Option<Self::Item> {
|
||||||
|
self.read().unwrap().get(idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> Option<usize> {
|
||||||
|
self.read().unwrap().len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: SequenceView + ?Sized> SequenceView for Arc<V> {
|
||||||
|
type Item = V::Item;
|
||||||
|
|
||||||
|
fn get(&self, idx: &usize) -> Option<Self::Item> {
|
||||||
|
self.deref().get(idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> Option<usize> {
|
||||||
|
self.deref().len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: SequenceView> SequenceView for Option<V> {
|
||||||
|
type Item = V::Item;
|
||||||
|
|
||||||
|
fn get(&self, idx: &usize) -> Option<Self::Item> {
|
||||||
|
(self.as_ref()? as &V).get(idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> Option<usize> {
|
||||||
|
if let Some(v) = self.as_ref() {
|
||||||
|
v.len()
|
||||||
|
} else {
|
||||||
|
Some(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
97
nested/src/sequence/seq2idx.rs
Normal file
97
nested/src/sequence/seq2idx.rs
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
use {
|
||||||
|
std::{
|
||||||
|
sync::Arc
|
||||||
|
},
|
||||||
|
std::sync::RwLock,
|
||||||
|
crate::{
|
||||||
|
core::{
|
||||||
|
View, Observer, ObserverBroadcast,
|
||||||
|
ViewPort, InnerViewPort, OuterViewPort
|
||||||
|
},
|
||||||
|
sequence::SequenceView,
|
||||||
|
index::{IndexArea, IndexView},
|
||||||
|
grid::GridView
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Transforms a SequenceView into IndexView<usize>
|
||||||
|
pub struct Sequence2Index<SrcView>
|
||||||
|
where SrcView: SequenceView + ?Sized + 'static {
|
||||||
|
src_view: Option<Arc<SrcView>>,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn IndexView<usize, Item = SrcView::Item>>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SrcView> Sequence2Index<SrcView>
|
||||||
|
where SrcView: SequenceView + ?Sized + 'static {
|
||||||
|
pub fn new(
|
||||||
|
port: InnerViewPort<dyn IndexView<usize, Item = SrcView::Item>>
|
||||||
|
) -> Arc<RwLock<Self>> {
|
||||||
|
let s2i = Arc::new(RwLock::new(
|
||||||
|
Sequence2Index {
|
||||||
|
src_view: None,
|
||||||
|
cast: port.get_broadcast()
|
||||||
|
}
|
||||||
|
));
|
||||||
|
port.set_view(Some(s2i.clone()));
|
||||||
|
s2i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Item: 'static> OuterViewPort<dyn SequenceView<Item = Item>> {
|
||||||
|
pub fn to_index(&self) -> OuterViewPort<dyn IndexView<usize, Item = Item>> {
|
||||||
|
let port = ViewPort::new();
|
||||||
|
port.add_update_hook(Arc::new(self.0.clone()));
|
||||||
|
self.add_observer(Sequence2Index::new(port.inner()));
|
||||||
|
port.into_outer()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_grid_horizontal(&self) -> OuterViewPort<dyn GridView<Item = Item>> {
|
||||||
|
self.to_index().to_grid_horizontal()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_grid_vertical(&self) -> OuterViewPort<dyn GridView<Item = Item>> {
|
||||||
|
self.to_index().to_grid_vertical()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SrcView> View for Sequence2Index<SrcView>
|
||||||
|
where SrcView: SequenceView + ?Sized + 'static {
|
||||||
|
type Msg = IndexArea<usize>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SrcView> IndexView<usize> for Sequence2Index<SrcView>
|
||||||
|
where SrcView: SequenceView + ?Sized + 'static {
|
||||||
|
type Item = SrcView::Item;
|
||||||
|
|
||||||
|
fn get(&self, key: &usize) -> Option<Self::Item> {
|
||||||
|
self.src_view.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn area(&self) -> IndexArea<usize> {
|
||||||
|
if let Some(len) = self.src_view.len() {
|
||||||
|
if len > 0 {
|
||||||
|
IndexArea::Range(0 ..= len-1)
|
||||||
|
} else {
|
||||||
|
IndexArea::Empty
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
IndexArea::Full
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SrcView> Observer<SrcView> for Sequence2Index<SrcView>
|
||||||
|
where SrcView: SequenceView + ?Sized + 'static {
|
||||||
|
fn reset(&mut self, view: Option<Arc<SrcView>>) {
|
||||||
|
let old_area = self.area();
|
||||||
|
self.src_view = view;
|
||||||
|
|
||||||
|
self.cast.notify(&old_area);
|
||||||
|
self.cast.notify(&self.area());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&mut self, idx: &usize) {
|
||||||
|
self.cast.notify(&IndexArea::Set(vec![ *idx ]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
70
nested/src/singleton/buffer.rs
Normal file
70
nested/src/singleton/buffer.rs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
use {
|
||||||
|
std::{
|
||||||
|
sync::{Arc}
|
||||||
|
},
|
||||||
|
std::sync::RwLock,
|
||||||
|
crate::{
|
||||||
|
core::{
|
||||||
|
Observer,
|
||||||
|
ObserverBroadcast,
|
||||||
|
View,
|
||||||
|
InnerViewPort
|
||||||
|
},
|
||||||
|
singleton::{SingletonView}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct SingletonBufferView<T: Clone + Eq + Send + Sync + 'static>(Arc<RwLock<T>>);
|
||||||
|
|
||||||
|
impl<T> View for SingletonBufferView<T>
|
||||||
|
where T: Clone + Eq + Send + Sync + 'static {
|
||||||
|
type Msg = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> SingletonView for SingletonBufferView<T>
|
||||||
|
where T: Clone + Eq + Send + Sync + 'static {
|
||||||
|
type Item = T;
|
||||||
|
|
||||||
|
fn get(&self) -> Self::Item {
|
||||||
|
self.0.read().unwrap().clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SingletonBuffer<T>
|
||||||
|
where T: Clone + Eq + Send + Sync + 'static {
|
||||||
|
value: Arc<RwLock<T>>,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn SingletonView<Item = T>>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> SingletonBuffer<T>
|
||||||
|
where T: Clone + Eq + Send + Sync + 'static {
|
||||||
|
pub fn new(
|
||||||
|
value: T,
|
||||||
|
port: InnerViewPort<dyn SingletonView<Item = T>>
|
||||||
|
) -> Self {
|
||||||
|
let value = Arc::new(RwLock::new(value));
|
||||||
|
port.set_view(Some(Arc::new(SingletonBufferView(value.clone()))));
|
||||||
|
|
||||||
|
SingletonBuffer {
|
||||||
|
value,
|
||||||
|
cast: port.get_broadcast()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self) -> T {
|
||||||
|
self.value.read().unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&mut self, new_value: T) {
|
||||||
|
let mut v = self.value.write().unwrap();
|
||||||
|
if *v != new_value {
|
||||||
|
*v = new_value;
|
||||||
|
drop(v);
|
||||||
|
self.cast.notify(&());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: impl Deref & DerefMut
|
||||||
|
|
84
nested/src/singleton/map.rs
Normal file
84
nested/src/singleton/map.rs
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
use {
|
||||||
|
std::sync::Arc,
|
||||||
|
std::sync::RwLock,
|
||||||
|
crate::{
|
||||||
|
singleton::{SingletonView},
|
||||||
|
core::{
|
||||||
|
Observer, ObserverBroadcast,
|
||||||
|
View, ViewPort, OuterViewPort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<Item: 'static> OuterViewPort<dyn SingletonView<Item = Item>> {
|
||||||
|
pub fn map<
|
||||||
|
DstItem: 'static,
|
||||||
|
F: Fn(Item) -> DstItem + Send + Sync + 'static
|
||||||
|
>(
|
||||||
|
&self,
|
||||||
|
f: F
|
||||||
|
) -> OuterViewPort<dyn SingletonView<Item = DstItem>> {
|
||||||
|
let port = ViewPort::new();
|
||||||
|
port.add_update_hook(Arc::new(self.0.clone()));
|
||||||
|
|
||||||
|
let map = Arc::new(RwLock::new(MapSingleton {
|
||||||
|
src_view: None,
|
||||||
|
f,
|
||||||
|
cast: port.inner().get_broadcast()
|
||||||
|
}));
|
||||||
|
|
||||||
|
self.add_observer(map.clone());
|
||||||
|
port.inner().set_view(Some(map));
|
||||||
|
port.into_outer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub struct MapSingleton<DstItem, SrcView, F>
|
||||||
|
where SrcView: SingletonView + ?Sized,
|
||||||
|
F: Fn(SrcView::Item) -> DstItem + Send + Sync
|
||||||
|
{
|
||||||
|
src_view: Option<Arc<SrcView>>,
|
||||||
|
f: F,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn SingletonView<Item = DstItem>>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<DstItem, SrcView, F> View for MapSingleton<DstItem, SrcView, F>
|
||||||
|
where SrcView: SingletonView + ?Sized,
|
||||||
|
F: Fn(SrcView::Item) -> DstItem + Send + Sync
|
||||||
|
{
|
||||||
|
type Msg = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<DstItem, SrcView, F> SingletonView for MapSingleton<DstItem, SrcView, F>
|
||||||
|
where SrcView: SingletonView + ?Sized,
|
||||||
|
F: Fn(SrcView::Item) -> DstItem + Send + Sync
|
||||||
|
{
|
||||||
|
type Item = DstItem;
|
||||||
|
|
||||||
|
fn get(&self) -> DstItem {
|
||||||
|
(self.f)(self.src_view.as_ref().unwrap().get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<DstItem, SrcView, F> Observer<SrcView> for MapSingleton<DstItem, SrcView, F>
|
||||||
|
where SrcView: SingletonView + ?Sized,
|
||||||
|
F: Fn(SrcView::Item) -> DstItem + Send + Sync
|
||||||
|
{
|
||||||
|
fn reset(&mut self, view: Option<Arc<SrcView>>) {
|
||||||
|
self.src_view = view;
|
||||||
|
self.cast.notify(&());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&mut self, msg: &()) {
|
||||||
|
self.cast.notify(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
68
nested/src/singleton/mod.rs
Normal file
68
nested/src/singleton/mod.rs
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
|
||||||
|
pub mod buffer;
|
||||||
|
pub mod map;
|
||||||
|
pub mod to_index;
|
||||||
|
//pub mod unwrap;
|
||||||
|
|
||||||
|
use {
|
||||||
|
std::{
|
||||||
|
sync::Arc,
|
||||||
|
ops::Deref
|
||||||
|
},
|
||||||
|
std::sync::RwLock,
|
||||||
|
crate::core::{View}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub use buffer::SingletonBuffer;
|
||||||
|
|
||||||
|
// TODO: #[ImplForArc, ImplForRwLock]
|
||||||
|
pub trait SingletonView : View<Msg = ()> {
|
||||||
|
type Item;
|
||||||
|
|
||||||
|
fn get(&self) -> Self::Item;
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<V: SingletonView + ?Sized> SingletonView for RwLock<V> {
|
||||||
|
type Item = V::Item;
|
||||||
|
|
||||||
|
fn get(&self) -> Self::Item {
|
||||||
|
self.read().unwrap().get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: SingletonView + ?Sized> SingletonView for Arc<V> {
|
||||||
|
type Item = V::Item;
|
||||||
|
|
||||||
|
fn get(&self) -> Self::Item {
|
||||||
|
self.deref().get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: SingletonView> SingletonView for Option<V>
|
||||||
|
where V::Item: Default{
|
||||||
|
type Item = V::Item;
|
||||||
|
|
||||||
|
fn get(&self) -> Self::Item {
|
||||||
|
if let Some(s) = self.as_ref() {
|
||||||
|
s.get()
|
||||||
|
} else {
|
||||||
|
V::Item::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
/*
|
||||||
|
impl<T> OuterViewPort<dyn SingletonView<Item = T>> {
|
||||||
|
pub fn get(&self) -> T {
|
||||||
|
self.get_view().unrwap().read().unwrap().get();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map<U: Send + Sync + 'static>(&self, f: impl Fn(T) -> U) -> OuterViewPort<dyn SingletonView<Item = U>> {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
86
nested/src/singleton/to_index.rs
Normal file
86
nested/src/singleton/to_index.rs
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
use {
|
||||||
|
std::sync::Arc,
|
||||||
|
std::sync::RwLock,
|
||||||
|
crate::{
|
||||||
|
singleton::{SingletonView},
|
||||||
|
index::{IndexArea, IndexView},
|
||||||
|
grid::{GridView},
|
||||||
|
core::{
|
||||||
|
Observer, ObserverBroadcast,
|
||||||
|
View, ViewPort, OuterViewPort
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<Item: 'static> OuterViewPort<dyn SingletonView<Item = Item>> {
|
||||||
|
pub fn to_index(&self) -> OuterViewPort<dyn IndexView<(), Item = Item>> {
|
||||||
|
let port = ViewPort::new();
|
||||||
|
port.add_update_hook(Arc::new(self.0.clone()));
|
||||||
|
|
||||||
|
let map = Arc::new(RwLock::new(Singleton2Index {
|
||||||
|
src_view: None,
|
||||||
|
cast: port.inner().get_broadcast()
|
||||||
|
}));
|
||||||
|
|
||||||
|
self.add_observer(map.clone());
|
||||||
|
port.inner().set_view(Some(map));
|
||||||
|
port.into_outer()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_grid(&self) -> OuterViewPort<dyn GridView<Item = Item>> {
|
||||||
|
self.to_index()
|
||||||
|
.map_key(
|
||||||
|
|_msg: &()| cgmath::Point2::new(0, 0),
|
||||||
|
|pt| if pt.x == 0 && pt.y == 0 { Some(()) } else { None }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub struct Singleton2Index<SrcView>
|
||||||
|
where SrcView: SingletonView + ?Sized
|
||||||
|
{
|
||||||
|
src_view: Option<Arc<SrcView>>,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn IndexView<(), Item = SrcView::Item>>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<SrcView> View for Singleton2Index<SrcView>
|
||||||
|
where SrcView: SingletonView + ?Sized
|
||||||
|
{
|
||||||
|
type Msg = IndexArea<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<SrcView> IndexView<()> for Singleton2Index<SrcView>
|
||||||
|
where SrcView: SingletonView + ?Sized
|
||||||
|
{
|
||||||
|
type Item = SrcView::Item;
|
||||||
|
|
||||||
|
fn area(&self) -> IndexArea<()> {
|
||||||
|
IndexArea::Full
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, _msg: &()) -> Option<Self::Item> {
|
||||||
|
Some(self.src_view.as_ref().unwrap().get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<SrcView> Observer<SrcView> for Singleton2Index<SrcView>
|
||||||
|
where SrcView: SingletonView + ?Sized
|
||||||
|
{
|
||||||
|
fn reset(&mut self, view: Option<Arc<SrcView>>) {
|
||||||
|
self.src_view = view;
|
||||||
|
self.cast.notify(&IndexArea::Full);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&mut self, _: &()) {
|
||||||
|
self.cast.notify(&IndexArea::Full);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
128
nested/src/string_editor.rs
Normal file
128
nested/src/string_editor.rs
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
use {
|
||||||
|
std::sync::Arc,
|
||||||
|
std::sync::RwLock,
|
||||||
|
termion::event::{Key, Event},
|
||||||
|
crate::{
|
||||||
|
core::{ViewPort, OuterViewPort},
|
||||||
|
singleton::{SingletonView, SingletonBuffer},
|
||||||
|
sequence::{SequenceView},
|
||||||
|
terminal::{TerminalView, TerminalStyle, TerminalEvent, TerminalEditor, TerminalEditorResult},
|
||||||
|
list::{ListEditor, sexpr::ListDecoration},
|
||||||
|
tree_nav::{TreeNav, TreeNavResult, TreeCursor}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub struct CharEditor {
|
||||||
|
data: SingletonBuffer<Option<char>>,
|
||||||
|
data_port: ViewPort<dyn SingletonView<Item = Option<char>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CharEditor {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut data_port = ViewPort::new();
|
||||||
|
CharEditor {
|
||||||
|
data: SingletonBuffer::new(None, data_port.inner()),
|
||||||
|
data_port
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_data_port(&self) -> OuterViewPort<dyn SingletonView<Item = Option<char>>> {
|
||||||
|
self.data_port.outer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TreeNav for CharEditor {}
|
||||||
|
impl TerminalEditor for CharEditor {
|
||||||
|
fn get_term_view(&self) -> OuterViewPort<dyn TerminalView> {
|
||||||
|
crate::terminal::make_label(
|
||||||
|
&if let Some(c) = self.data.get() {
|
||||||
|
c.to_string()
|
||||||
|
} else {
|
||||||
|
"".to_string()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_terminal_event(&mut self, event: &TerminalEvent) -> TerminalEditorResult {
|
||||||
|
match event {
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Char('\n'))) =>
|
||||||
|
TerminalEditorResult::Continue,
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Char(c))) => {
|
||||||
|
self.data.set(Some(*c));
|
||||||
|
TerminalEditorResult::Exit
|
||||||
|
}
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Backspace)) |
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Delete)) => {
|
||||||
|
self.data.set(None);
|
||||||
|
TerminalEditorResult::Exit
|
||||||
|
}
|
||||||
|
_ => TerminalEditorResult::Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub struct StringEditor {
|
||||||
|
chars_editor: ListEditor< CharEditor,
|
||||||
|
Box<dyn Fn() -> Arc<RwLock<CharEditor>> + Send + Sync + 'static> >
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StringEditor {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
StringEditor {
|
||||||
|
chars_editor: ListEditor::new(
|
||||||
|
Box::new(
|
||||||
|
move || {
|
||||||
|
Arc::new(RwLock::new(CharEditor::new()))
|
||||||
|
}
|
||||||
|
),
|
||||||
|
crate::list::ListEditorStyle::String
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_data_port(&self) -> OuterViewPort<dyn SequenceView<Item = char>> {
|
||||||
|
self.chars_editor.get_data_port()
|
||||||
|
.map(
|
||||||
|
|char_editor| char_editor.read().unwrap().data.get().unwrap()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TreeNav for StringEditor {
|
||||||
|
fn get_cursor(&self) -> TreeCursor { self.chars_editor.get_cursor() }
|
||||||
|
fn goto(&mut self, cur: TreeCursor) -> TreeNavResult { self.chars_editor.goto(cur) }
|
||||||
|
fn goto_home(&mut self) -> TreeNavResult { self.chars_editor.goto_home() }
|
||||||
|
fn goto_end(&mut self) -> TreeNavResult { self.chars_editor.goto_end() }
|
||||||
|
fn pxev(&mut self) -> TreeNavResult { self.chars_editor.pxev() }
|
||||||
|
fn nexd(&mut self) -> TreeNavResult { self.chars_editor.nexd() }
|
||||||
|
fn up(&mut self) -> TreeNavResult { self.chars_editor.up() }
|
||||||
|
fn dn(&mut self) -> TreeNavResult { TreeNavResult::Exit }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TerminalEditor for StringEditor {
|
||||||
|
fn get_term_view(&self) -> OuterViewPort<dyn TerminalView> {
|
||||||
|
self.chars_editor
|
||||||
|
.get_seg_seq_view()
|
||||||
|
.decorate("\"", "\"", "", 1)
|
||||||
|
.to_grid_horizontal()
|
||||||
|
.flatten()
|
||||||
|
.map_item(
|
||||||
|
|_idx, atom|
|
||||||
|
atom.add_style_back(TerminalStyle::fg_color((120, 200, 10)))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_terminal_event(&mut self, event: &TerminalEvent) -> TerminalEditorResult {
|
||||||
|
match event {
|
||||||
|
TerminalEvent::Input(Event::Key(Key::Char('\n'))) => {
|
||||||
|
self.chars_editor.up();
|
||||||
|
TerminalEditorResult::Exit
|
||||||
|
}
|
||||||
|
event => self.chars_editor.handle_terminal_event(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,45 +1,36 @@
|
||||||
use {
|
use {
|
||||||
//async_std::{io::{Read, ReadExt}},
|
std::{
|
||||||
r3vi::{
|
sync::{Arc, RwLock},
|
||||||
view::{
|
pin::Pin,
|
||||||
InnerViewPort, Observer, ObserverBroadcast, OuterViewPort, View, ViewPort,
|
fs::File,
|
||||||
index::*,
|
os::unix::io::FromRawFd
|
||||||
singleton::*,
|
|
||||||
},
|
|
||||||
buffer::{singleton::*, index_hashmap::*},
|
|
||||||
projection::projection_helper::ProjectionHelper,
|
|
||||||
},
|
},
|
||||||
crate::{
|
|
||||||
terminal::{TerminalAtom, TerminalStyle, TerminalView},
|
|
||||||
},
|
|
||||||
cgmath::{Point2, Vector2},
|
|
||||||
std::io::Read,
|
std::io::Read,
|
||||||
std::sync::{Arc, RwLock},
|
//async_std::{io::{Read, ReadExt}},
|
||||||
vte::{Params, Parser, Perform},
|
crate::{
|
||||||
|
core::{InnerViewPort, OuterViewPort},
|
||||||
|
terminal::{
|
||||||
|
TerminalAtom,
|
||||||
|
TerminalStyle,
|
||||||
|
TerminalView
|
||||||
|
},
|
||||||
|
index::buffer::IndexBuffer
|
||||||
|
},
|
||||||
|
cgmath::Point2,
|
||||||
|
vte::{Params, Parser, Perform}
|
||||||
};
|
};
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
pub fn read_ansi_from<R: Read + Unpin>(ansi_reader: &mut R, port: InnerViewPort<dyn TerminalView>) {
|
||||||
|
|
||||||
pub fn read_ansi_from<R: Read + Unpin>(
|
|
||||||
ansi_reader: &mut R,
|
|
||||||
max_size: Vector2<i16>,
|
|
||||||
port: InnerViewPort<dyn TerminalView>,
|
|
||||||
) {
|
|
||||||
let mut statemachine = Parser::new();
|
let mut statemachine = Parser::new();
|
||||||
|
|
||||||
let buf_port = ViewPort::new();
|
|
||||||
let size_port = ViewPort::<dyn SingletonView<Item = Vector2<i16>>>::new();
|
|
||||||
let cursor_port = ViewPort::<dyn SingletonView<Item = Point2<i16>>>::new();
|
|
||||||
let offset_port = ViewPort::<dyn SingletonView<Item = Vector2<i16>>>::new();
|
|
||||||
|
|
||||||
let mut performer = PerfAtom {
|
let mut performer = PerfAtom {
|
||||||
buf: IndexBuffer::with_port(buf_port.inner()),
|
cursor: Point2::new(0, 0),
|
||||||
size: SingletonBuffer::with_port(max_size, size_port.inner()),
|
style: TerminalStyle::default(),
|
||||||
offset: SingletonBuffer::with_port(Vector2::new(0, 0), offset_port.inner()),
|
term_width: 180,
|
||||||
cursor: SingletonBuffer::with_port(Point2::new(0, 0), cursor_port.inner()),
|
|
||||||
cursty: TerminalStyle::default(),
|
cursor_stack: Vec::new(),
|
||||||
curinv: false,
|
|
||||||
cursav: Point2::new(0, 0),
|
buf: IndexBuffer::new(port),
|
||||||
|
|
||||||
colors: ColorPalett {
|
colors: ColorPalett {
|
||||||
black: (1, 1, 1),
|
black: (1, 1, 1),
|
||||||
|
@ -49,59 +40,28 @@ pub fn read_ansi_from<R: Read + Unpin>(
|
||||||
blue: (0, 111, 184),
|
blue: (0, 111, 184),
|
||||||
magenta: (118, 38, 113),
|
magenta: (118, 38, 113),
|
||||||
cyan: (44, 181, 233),
|
cyan: (44, 181, 233),
|
||||||
white: (204, 204, 204),
|
white: (204, 204, 204)
|
||||||
},
|
}
|
||||||
|
|
||||||
_pty_proj: PtyView::new(
|
|
||||||
buf_port.outer(),
|
|
||||||
cursor_port.outer().map(|x| Some(x)),
|
|
||||||
offset_port.outer().map(|x| Some(x)),
|
|
||||||
size_port.outer().map(|x| Some(x)),
|
|
||||||
port,
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut buf = [0; 2048];
|
let mut buf = [0; 2048];
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match ansi_reader.read(&mut buf) {
|
match ansi_reader.read(&mut buf) {
|
||||||
Ok(0) => break,
|
//Ok(0) => break,
|
||||||
Ok(n) => {
|
Ok(n) => {
|
||||||
for byte in &buf[..n] {
|
for byte in &buf[..n] {
|
||||||
statemachine.advance(&mut performer, *byte);
|
statemachine.advance(&mut performer, *byte);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
Err(_err) => {
|
Err(err) => {
|
||||||
//println!("err: {}", err);
|
//println!("err: {}", err);
|
||||||
break;
|
break;
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
|
|
||||||
enum TTYColor {
|
|
||||||
Rgb(u8, u8, u8),
|
|
||||||
|
|
||||||
// 3-Bit/4-Bit colors
|
|
||||||
Black,
|
|
||||||
LightBlack,
|
|
||||||
Red,
|
|
||||||
LightRed,
|
|
||||||
Green,
|
|
||||||
LightGreen,
|
|
||||||
Yellow,
|
|
||||||
LightYellow,
|
|
||||||
Blue,
|
|
||||||
LightBlue,
|
|
||||||
Magenta,
|
|
||||||
LightMagenta,
|
|
||||||
Cyan,
|
|
||||||
LightCyan,
|
|
||||||
White,
|
|
||||||
LightWhite,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ColorPalett {
|
struct ColorPalett {
|
||||||
black: (u8, u8, u8),
|
black: (u8, u8, u8),
|
||||||
|
@ -111,325 +71,78 @@ struct ColorPalett {
|
||||||
blue: (u8, u8, u8),
|
blue: (u8, u8, u8),
|
||||||
magenta: (u8, u8, u8),
|
magenta: (u8, u8, u8),
|
||||||
cyan: (u8, u8, u8),
|
cyan: (u8, u8, u8),
|
||||||
white: (u8, u8, u8),
|
white: (u8, u8, u8)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ColorPalett {
|
|
||||||
fn get_rgb(&self, col: &TTYColor) -> (u8, u8, u8) {
|
|
||||||
match col {
|
|
||||||
TTYColor::Rgb(r, g, b) => (*r, *g, *b),
|
|
||||||
TTYColor::Black | TTYColor::LightBlack => self.black,
|
|
||||||
TTYColor::Red | TTYColor::LightRed => self.red,
|
|
||||||
TTYColor::Green | TTYColor::LightGreen => self.green,
|
|
||||||
TTYColor::Yellow | TTYColor::LightYellow => self.yellow,
|
|
||||||
TTYColor::Blue | TTYColor::LightBlue => self.blue,
|
|
||||||
TTYColor::Magenta | TTYColor::LightMagenta => self.magenta,
|
|
||||||
TTYColor::Cyan | TTYColor::LightCyan => self.cyan,
|
|
||||||
TTYColor::White | TTYColor::LightWhite => self.white,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
|
|
||||||
/// Display Cursor & scrolling
|
|
||||||
struct PtyView {
|
|
||||||
buf: Arc<dyn IndexView<Point2<i16>, Item = TerminalAtom>>,
|
|
||||||
curpos: Arc<dyn SingletonView<Item = Option<Point2<i16>>>>,
|
|
||||||
offset: Arc<dyn SingletonView<Item = Option<Vector2<i16>>>>,
|
|
||||||
size: Arc<dyn SingletonView<Item = Option<Vector2<i16>>>>,
|
|
||||||
|
|
||||||
old_offset: Vector2<i16>,
|
|
||||||
old_curpos: Point2<i16>,
|
|
||||||
old_size: Vector2<i16>,
|
|
||||||
|
|
||||||
max_pt: Point2<i16>,
|
|
||||||
|
|
||||||
cast: Arc<RwLock<ObserverBroadcast<dyn TerminalView>>>,
|
|
||||||
proj_helper: ProjectionHelper<usize, Self>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl View for PtyView {
|
|
||||||
type Msg = IndexArea<Point2<i16>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IndexView<Point2<i16>> for PtyView {
|
|
||||||
type Item = TerminalAtom;
|
|
||||||
|
|
||||||
fn get(&self, pt: &Point2<i16>) -> Option<TerminalAtom> {
|
|
||||||
let atom = self.buf.get(&(pt + self.old_offset));
|
|
||||||
if self.old_curpos == *pt {
|
|
||||||
if let Some(mut a) = atom {
|
|
||||||
let bg_col = a.style.fg_color.unwrap_or((255, 255, 255));
|
|
||||||
let fg_col = a.style.bg_color.unwrap_or((0, 0, 0));
|
|
||||||
a.style.fg_color = Some(fg_col);
|
|
||||||
a.style.bg_color = Some(bg_col);
|
|
||||||
Some(a)
|
|
||||||
} else {
|
|
||||||
Some(TerminalAtom::new(
|
|
||||||
' ',
|
|
||||||
TerminalStyle::bg_color((255, 255, 255)),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
atom
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn area(&self) -> IndexArea<Point2<i16>> {
|
|
||||||
IndexArea::Range(
|
|
||||||
Point2::new(0, 0)
|
|
||||||
..=Point2::new(
|
|
||||||
std::cmp::max(self.old_curpos.x, self.max_pt.x),
|
|
||||||
std::cmp::max(self.old_curpos.y, self.max_pt.y),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PtyView {
|
|
||||||
fn new(
|
|
||||||
buf_port: OuterViewPort<dyn IndexView<Point2<i16>, Item = TerminalAtom>>,
|
|
||||||
curpos_port: OuterViewPort<dyn SingletonView<Item = Option<Point2<i16>>>>,
|
|
||||||
offset_port: OuterViewPort<dyn SingletonView<Item = Option<Vector2<i16>>>>,
|
|
||||||
size_port: OuterViewPort<dyn SingletonView<Item = Option<Vector2<i16>>>>,
|
|
||||||
|
|
||||||
out_port: InnerViewPort<dyn TerminalView>,
|
|
||||||
) -> Arc<RwLock<Self>> {
|
|
||||||
let mut proj_helper = ProjectionHelper::new(out_port.0.update_hooks.clone());
|
|
||||||
let proj = Arc::new(RwLock::new(PtyView {
|
|
||||||
old_curpos: Point2::new(0, 0),
|
|
||||||
old_size: Vector2::new(0, 0),
|
|
||||||
old_offset: Vector2::new(0, 0),
|
|
||||||
max_pt: Point2::new(0, 0),
|
|
||||||
|
|
||||||
curpos: proj_helper.new_singleton_arg(0, curpos_port, |s: &mut Self, _msg| {
|
|
||||||
s.cast.notify(&IndexArea::Set(vec![s.old_curpos]));
|
|
||||||
s.old_curpos = s.curpos.get().unwrap_or(Point2::new(0, 0));
|
|
||||||
s.cast.notify(&IndexArea::Set(vec![s.old_curpos]));
|
|
||||||
}),
|
|
||||||
|
|
||||||
offset: proj_helper.new_singleton_arg(1, offset_port, |s: &mut Self, _msg| {
|
|
||||||
// todo
|
|
||||||
let new_offset = s.offset.get().unwrap_or(Vector2::new(0, 0));
|
|
||||||
if s.old_offset != new_offset {
|
|
||||||
s.old_offset = new_offset;
|
|
||||||
s.cast.notify(&s.area());
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
size: proj_helper.new_singleton_arg(2, size_port, |s: &mut Self, _msg| {
|
|
||||||
let new_size = s.size.get().unwrap_or(Vector2::new(0, 0));
|
|
||||||
if s.old_size != new_size {
|
|
||||||
s.old_size = new_size;
|
|
||||||
s.cast.notify(&s.area());
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
buf: proj_helper.new_index_arg(3, buf_port, |s: &mut Self, area| {
|
|
||||||
let size = s.old_size;
|
|
||||||
let area = area.map(|pt| *pt - s.old_offset);
|
|
||||||
|
|
||||||
if s.max_pt.x < size.x || s.max_pt.y < size.y {
|
|
||||||
match &area {
|
|
||||||
IndexArea::Empty => {}
|
|
||||||
IndexArea::Full => {}
|
|
||||||
IndexArea::Range(_) => {}
|
|
||||||
IndexArea::Set(v) => {
|
|
||||||
let mx = v.iter().map(|pt| pt.x).max().unwrap_or(0);
|
|
||||||
if mx > s.max_pt.x && mx < size.x {
|
|
||||||
s.max_pt.x = mx;
|
|
||||||
}
|
|
||||||
|
|
||||||
let my = v.iter().map(|pt| pt.y).max().unwrap_or(0);
|
|
||||||
if my > s.max_pt.y && my < size.y {
|
|
||||||
s.max_pt.y = my;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s.cast.notify(&area);
|
|
||||||
}),
|
|
||||||
|
|
||||||
cast: out_port.get_broadcast(),
|
|
||||||
proj_helper,
|
|
||||||
}));
|
|
||||||
|
|
||||||
proj.write().unwrap().proj_helper.set_proj(&proj);
|
|
||||||
out_port.set_view(Some(proj.clone()));
|
|
||||||
|
|
||||||
proj
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
|
|
||||||
struct PerfAtom {
|
struct PerfAtom {
|
||||||
buf: IndexBuffer<Point2<i16>, TerminalAtom>,
|
|
||||||
size: SingletonBuffer<Vector2<i16>>,
|
|
||||||
offset: SingletonBuffer<Vector2<i16>>,
|
|
||||||
cursor: SingletonBuffer<Point2<i16>>,
|
|
||||||
cursty: TerminalStyle,
|
|
||||||
curinv: bool,
|
|
||||||
cursav: Point2<i16>,
|
|
||||||
|
|
||||||
colors: ColorPalett,
|
colors: ColorPalett,
|
||||||
|
term_width: i16,
|
||||||
|
|
||||||
_pty_proj: Arc<RwLock<PtyView>>,
|
cursor: Point2<i16>,
|
||||||
|
style: TerminalStyle,
|
||||||
|
|
||||||
|
cursor_stack: Vec<Point2<i16>>,
|
||||||
|
|
||||||
|
buf: IndexBuffer<Point2<i16>, TerminalAtom>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PerfAtom {
|
impl PerfAtom {
|
||||||
fn write_atom(&mut self, pos: Point2<i16>, atom: Option<TerminalAtom>) {
|
fn write_atom(&mut self, pos: Point2<i16>, atom: Option<TerminalAtom>) {
|
||||||
if let Some(a) = atom {
|
if let Some(a) = atom {
|
||||||
self.buf.insert(pos + self.offset.get(), a);
|
self.buf.insert(pos, a);
|
||||||
} else {
|
} else {
|
||||||
self.buf.remove(pos);
|
self.buf.remove(pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_style(&self) -> TerminalStyle {
|
|
||||||
let mut style = self.cursty;
|
|
||||||
if self.curinv {
|
|
||||||
style.fg_color = Some(self.cursty.bg_color.unwrap_or(self.colors.black));
|
|
||||||
style.bg_color = Some(self.cursty.fg_color.unwrap_or(self.colors.white));
|
|
||||||
}
|
|
||||||
style
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_fg_color(&mut self, col: &TTYColor) {
|
|
||||||
self.cursty = self
|
|
||||||
.cursty
|
|
||||||
.add(TerminalStyle::fg_color(self.colors.get_rgb(col)));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_bg_color(&mut self, col: &TTYColor) {
|
|
||||||
self.cursty = self
|
|
||||||
.cursty
|
|
||||||
.add(TerminalStyle::bg_color(self.colors.get_rgb(col)));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn linefeed(&mut self) {
|
|
||||||
let size = self.size.get();
|
|
||||||
let mut c = self.cursor.get_mut();
|
|
||||||
c.x = 0;
|
|
||||||
|
|
||||||
if c.y + 1 >= size.y {
|
|
||||||
self.scroll_up(1);
|
|
||||||
} else {
|
|
||||||
c.y += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn carriage_return(&mut self) {
|
|
||||||
let mut c = self.cursor.get_mut();
|
|
||||||
c.x = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn horizontal_tab(&mut self) {
|
|
||||||
let mut c = self.cursor.get_mut();
|
|
||||||
c.x += 8 - (c.x % 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn backspace(&mut self) {
|
|
||||||
self.write_atom(self.cursor.get(), None);
|
|
||||||
let mut c = self.cursor.get_mut();
|
|
||||||
c.x -= 1;
|
|
||||||
if c.x < 0 {
|
|
||||||
c.y -= 0;
|
|
||||||
c.x = self.size.get().x - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cursor_up(&mut self, n: usize) {
|
|
||||||
if self.cursor.get().y <= 0 {
|
|
||||||
self.scroll_dn(1);
|
|
||||||
} else {
|
|
||||||
self.cursor.get_mut().y -= n as i16;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cursor_dn(&mut self, n: usize) {
|
|
||||||
let mut c = self.cursor.get_mut();
|
|
||||||
let s = self.size.get();
|
|
||||||
|
|
||||||
if c.y + (n as i16) < s.y {
|
|
||||||
c.y += n as i16;
|
|
||||||
} else {
|
|
||||||
let r = (n as i16) - (s.y - 1 - c.y);
|
|
||||||
c.y = s.y - 1;
|
|
||||||
self.scroll_up(r as usize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cursor_pxev(&mut self, n: usize) {
|
|
||||||
let mut c = self.cursor.get_mut();
|
|
||||||
let _s = self.size.get();
|
|
||||||
|
|
||||||
if c.y - (n as i16) >= 0 {
|
|
||||||
c.y -= n as i16;
|
|
||||||
} else {
|
|
||||||
let r = (n as i16) - c.y;
|
|
||||||
c.y = 0;
|
|
||||||
self.scroll_dn(r as usize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cursor_nexd(&mut self, n: usize) {
|
|
||||||
let mut c = self.cursor.get_mut();
|
|
||||||
c.x += n as i16;
|
|
||||||
if c.x >= self.size.get().x {
|
|
||||||
c.y += c.x / self.size.get().x;
|
|
||||||
c.x %= self.size.get().x;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn insert_blank_lines(&mut self, _n: usize) {
|
|
||||||
//eprintln!("insert blank lines");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn scroll_up(&mut self, n: usize) {
|
|
||||||
self.offset.get_mut().y += n as i16;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn scroll_dn(&mut self, n: usize) {
|
|
||||||
self.offset.get_mut().y -= n as i16;
|
|
||||||
//self.cursor_up(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn save_cursor_position(&mut self) {
|
|
||||||
self.cursav = self.cursor.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn restore_cursor_position(&mut self) {
|
|
||||||
self.cursor.set(self.cursav);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Perform for PerfAtom {
|
impl Perform for PerfAtom {
|
||||||
fn print(&mut self, ch: char) {
|
fn print(&mut self, c: char) {
|
||||||
let mut c = self.cursor.get_mut();
|
//eprintln!("[print] {:?}", c);
|
||||||
self.write_atom(*c, Some(TerminalAtom::new(ch, self.get_style())));
|
self.write_atom(
|
||||||
|
self.cursor,
|
||||||
|
Some(TerminalAtom::new(c, self.style))
|
||||||
|
);
|
||||||
|
|
||||||
c.x += 1;
|
self.cursor.x += 1;
|
||||||
if c.x >= self.size.get().x {
|
if self.cursor.x > self.term_width {
|
||||||
self.linefeed();
|
self.cursor.x = 0;
|
||||||
|
self.cursor.y += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute(&mut self, byte: u8) {
|
fn execute(&mut self, byte: u8) {
|
||||||
|
//eprintln!("[execute] {:02x}", byte);
|
||||||
match byte {
|
match byte {
|
||||||
b'\n' => self.linefeed(),
|
// linefeed
|
||||||
b'\r' => self.carriage_return(),
|
b'\n' => {
|
||||||
b'\t' => self.horizontal_tab(),
|
self.cursor.x = 0;
|
||||||
0x8 => self.backspace(),
|
self.cursor.y += 1;
|
||||||
_ => {
|
},
|
||||||
//eprintln!("unhandled execute byte {:02x}", byte);
|
|
||||||
|
// carriage return
|
||||||
|
b'\r' => {
|
||||||
|
self.cursor.x = 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
// horizontal tab
|
||||||
|
b'\t' => {
|
||||||
|
self.cursor.x += 8 - (self.cursor.x % 8);
|
||||||
|
},
|
||||||
|
|
||||||
|
// backspace
|
||||||
|
0x8 => {
|
||||||
|
self.cursor.x -= 1;
|
||||||
|
if self.cursor.x < 0 {
|
||||||
|
self.cursor.y -= 0;
|
||||||
|
self.cursor.x = self.term_width - 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hook(&mut self, _params: &Params, _intermediates: &[u8], _ignore: bool, _c: char) {
|
fn hook(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) {
|
||||||
/*
|
/*
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[hook] params={:?}, intermediates={:?}, ignore={:?}, char={:?}",
|
"[hook] params={:?}, intermediates={:?}, ignore={:?}, char={:?}",
|
||||||
|
@ -438,340 +151,222 @@ impl Perform for PerfAtom {
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put(&mut self, _byte: u8) {
|
fn put(&mut self, byte: u8) {
|
||||||
/*
|
//eprintln!("[put] {:02x}", byte);
|
||||||
eprintln!("[put] {:02x}", byte);
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unhook(&mut self) {
|
fn unhook(&mut self) {
|
||||||
//eprintln!("[unhook]");
|
//eprintln!("[unhook]");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn osc_dispatch(&mut self, params: &[&[u8]], _bell_terminated: bool) {
|
fn osc_dispatch(&mut self, params: &[&[u8]], bell_terminated: bool) {
|
||||||
//eprintln!("[osc_dispatch] params={:?} bell_terminated={}", params, bell_terminated);
|
//eprintln!("[osc_dispatch] params={:?} bell_terminated={}", params, bell_terminated);
|
||||||
|
|
||||||
match params[0] {
|
|
||||||
// Reset foreground color
|
|
||||||
b"110" => self.set_fg_color(&TTYColor::White),
|
|
||||||
// Reset background color
|
|
||||||
b"111" => self.set_bg_color(&TTYColor::Black),
|
|
||||||
// Reset text cursor color
|
|
||||||
b"112" => {}
|
|
||||||
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn csi_dispatch(&mut self, params: &Params, _intermediates: &[u8], _ignore: bool, c: char) {
|
fn csi_dispatch(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) {
|
||||||
|
/*
|
||||||
|
eprintln!(
|
||||||
|
"[csi_dispatch] params={:#?}, intermediates={:?}, ignore={:?}, char={:?}",
|
||||||
|
params, intermediates, ignore, c
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
|
||||||
let mut piter = params.into_iter();
|
let mut piter = params.into_iter();
|
||||||
|
|
||||||
match c {
|
match c {
|
||||||
// Set SGR
|
// Set SGR
|
||||||
'm' => {
|
'm' => while let Some(n) = piter.next() {
|
||||||
while let Some(n) = piter.next() {
|
match n[0] {
|
||||||
match n[0] {
|
0 => self.style = TerminalStyle::default(),
|
||||||
0 => {
|
1 => self.style = self.style.add(TerminalStyle::bold(true)),
|
||||||
self.cursty = TerminalStyle::default();
|
3 => self.style = self.style.add(TerminalStyle::italic(true)),
|
||||||
self.curinv = false;
|
4 => self.style = self.style.add(TerminalStyle::underline(true)),
|
||||||
}
|
|
||||||
1 => self.cursty = self.cursty.add(TerminalStyle::bold(true)),
|
|
||||||
3 => self.cursty = self.cursty.add(TerminalStyle::italic(true)),
|
|
||||||
4 => self.cursty = self.cursty.add(TerminalStyle::underline(true)),
|
|
||||||
7 => self.curinv = true,
|
|
||||||
27 => self.curinv = false,
|
|
||||||
|
|
||||||
30 => self.set_fg_color(&TTYColor::Black),
|
30 => self.style = self.style.add(TerminalStyle::fg_color(self.colors.black)),
|
||||||
40 => self.set_bg_color(&TTYColor::Black),
|
40 => self.style = self.style.add(TerminalStyle::bg_color(self.colors.black)),
|
||||||
90 => self.set_fg_color(&TTYColor::LightBlack),
|
31 => self.style = self.style.add(TerminalStyle::fg_color(self.colors.red)),
|
||||||
100 => self.set_bg_color(&TTYColor::LightBlack),
|
41 => self.style = self.style.add(TerminalStyle::bg_color(self.colors.red)),
|
||||||
31 => self.set_fg_color(&TTYColor::Red),
|
32 => self.style = self.style.add(TerminalStyle::fg_color(self.colors.green)),
|
||||||
41 => self.set_bg_color(&TTYColor::Red),
|
42 => self.style = self.style.add(TerminalStyle::bg_color(self.colors.green)),
|
||||||
91 => self.set_fg_color(&TTYColor::LightRed),
|
33 => self.style = self.style.add(TerminalStyle::fg_color(self.colors.yellow)),
|
||||||
101 => self.set_bg_color(&TTYColor::LightRed),
|
43 => self.style = self.style.add(TerminalStyle::bg_color(self.colors.yellow)),
|
||||||
32 => self.set_fg_color(&TTYColor::Green),
|
34 => self.style = self.style.add(TerminalStyle::fg_color(self.colors.blue)),
|
||||||
42 => self.set_bg_color(&TTYColor::Green),
|
44 => self.style = self.style.add(TerminalStyle::bg_color(self.colors.blue)),
|
||||||
92 => self.set_fg_color(&TTYColor::LightGreen),
|
35 => self.style = self.style.add(TerminalStyle::fg_color(self.colors.magenta)),
|
||||||
102 => self.set_bg_color(&TTYColor::LightGreen),
|
45 => self.style = self.style.add(TerminalStyle::bg_color(self.colors.magenta)),
|
||||||
33 => self.set_fg_color(&TTYColor::Yellow),
|
36 => self.style = self.style.add(TerminalStyle::fg_color(self.colors.cyan)),
|
||||||
43 => self.set_bg_color(&TTYColor::Yellow),
|
46 => self.style = self.style.add(TerminalStyle::bg_color(self.colors.cyan)),
|
||||||
93 => self.set_fg_color(&TTYColor::LightYellow),
|
37 => self.style = self.style.add(TerminalStyle::fg_color(self.colors.white)),
|
||||||
103 => self.set_bg_color(&TTYColor::LightYellow),
|
47 => self.style = self.style.add(TerminalStyle::bg_color(self.colors.white)),
|
||||||
34 => self.set_fg_color(&TTYColor::Blue),
|
|
||||||
44 => self.set_bg_color(&TTYColor::Blue),
|
|
||||||
94 => self.set_fg_color(&TTYColor::LightBlue),
|
|
||||||
104 => self.set_bg_color(&TTYColor::LightBlue),
|
|
||||||
35 => self.set_fg_color(&TTYColor::Magenta),
|
|
||||||
45 => self.set_bg_color(&TTYColor::Magenta),
|
|
||||||
95 => self.set_fg_color(&TTYColor::LightMagenta),
|
|
||||||
105 => self.set_bg_color(&TTYColor::LightMagenta),
|
|
||||||
36 => self.set_fg_color(&TTYColor::Cyan),
|
|
||||||
46 => self.set_bg_color(&TTYColor::Cyan),
|
|
||||||
96 => self.set_fg_color(&TTYColor::LightCyan),
|
|
||||||
106 => self.set_bg_color(&TTYColor::LightCyan),
|
|
||||||
37 => self.set_fg_color(&TTYColor::White),
|
|
||||||
47 => self.set_bg_color(&TTYColor::White),
|
|
||||||
97 => self.set_fg_color(&TTYColor::LightWhite),
|
|
||||||
107 => self.set_bg_color(&TTYColor::LightWhite),
|
|
||||||
|
|
||||||
38 => {
|
38 => {
|
||||||
let x = piter.next().unwrap();
|
let x = piter.next().unwrap();
|
||||||
match x[0] {
|
match x[0] {
|
||||||
2 => {
|
2 => {
|
||||||
let r = piter.next().unwrap()[0] as u8;
|
let r = piter.next().unwrap();
|
||||||
let g = piter.next().unwrap()[0] as u8;
|
let g = piter.next().unwrap();
|
||||||
let b = piter.next().unwrap()[0] as u8;
|
let b = piter.next().unwrap();
|
||||||
self.set_fg_color(&TTYColor::Rgb(r, g, b));
|
self.style = self.style.add(TerminalStyle::fg_color((r[0] as u8, g[0] as u8, b[30] as u8)))
|
||||||
}
|
},
|
||||||
5 => {
|
5 => {
|
||||||
let v = piter.next().unwrap();
|
let v = piter.next().unwrap();
|
||||||
let rgb = ansi_colours::rgb_from_ansi256(v[0] as u8);
|
self.style = self.style.add(TerminalStyle::fg_color(ansi_colours::rgb_from_ansi256(v[0] as u8)))
|
||||||
self.set_fg_color(&TTYColor::Rgb(rgb.0, rgb.1, rgb.2));
|
},
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
48 => {
|
48 => {
|
||||||
let x = piter.next().unwrap();
|
let x = piter.next().unwrap();
|
||||||
match x[0] {
|
match x[0] {
|
||||||
2 => {
|
2 => {
|
||||||
let r = piter.next().unwrap()[0] as u8;
|
let r = piter.next().unwrap();
|
||||||
let g = piter.next().unwrap()[0] as u8;
|
let g = piter.next().unwrap();
|
||||||
let b = piter.next().unwrap()[0] as u8;
|
let b = piter.next().unwrap();
|
||||||
self.set_bg_color(&TTYColor::Rgb(r, g, b));
|
self.style = self.style.add(TerminalStyle::bg_color((r[0] as u8, g[0] as u8, b[30] as u8)))
|
||||||
}
|
},
|
||||||
5 => {
|
5 => {
|
||||||
let v = piter.next().unwrap();
|
let v = piter.next().unwrap();
|
||||||
let rgb = ansi_colours::rgb_from_ansi256(v[0] as u8);
|
self.style = self.style.add(TerminalStyle::bg_color(ansi_colours::rgb_from_ansi256(v[0] as u8)))
|
||||||
self.set_bg_color(&TTYColor::Rgb(rgb.0, rgb.1, rgb.2));
|
},
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
'@' => {
|
|
||||||
let n = piter.next().unwrap_or(&[1])[0] as usize;
|
'H' => {
|
||||||
for _ in 0..n {
|
if let Some(y) = piter.next() { self.cursor.y = y[0] as i16 - 1 };
|
||||||
self.print(' ');
|
if let Some(x) = piter.next() { self.cursor.x = x[0] as i16 - 1 };
|
||||||
}
|
|
||||||
}
|
//eprintln!("cursor at {:?}", self.cursor);
|
||||||
'A' => {
|
},
|
||||||
self.cursor_up(piter.next().unwrap_or(&[1])[0] as usize);
|
|
||||||
}
|
'A' => { self.cursor.y -= piter.next().unwrap()[0] as i16; }
|
||||||
'B' => {
|
'B' => { self.cursor.y += piter.next().unwrap()[0] as i16; }
|
||||||
self.cursor_dn(piter.next().unwrap_or(&[1])[0] as usize);
|
'C' => { self.cursor.x += piter.next().unwrap()[0] as i16; }
|
||||||
}
|
'D' => { self.cursor.x -= piter.next().unwrap()[0] as i16; }
|
||||||
'C' | 'a' => {
|
|
||||||
self.cursor_nexd(piter.next().unwrap_or(&[1])[0] as usize);
|
|
||||||
}
|
|
||||||
'D' => {
|
|
||||||
self.cursor_pxev(piter.next().unwrap_or(&[1])[0] as usize);
|
|
||||||
}
|
|
||||||
'd' => {
|
|
||||||
self.cursor.get_mut().y = piter.next().unwrap_or(&[1])[0] as i16 - 1;
|
|
||||||
}
|
|
||||||
'E' => {
|
'E' => {
|
||||||
let mut c = self.cursor.get_mut();
|
self.cursor.x = 0;
|
||||||
if c.x >= self.size.get().x {
|
self.cursor.y += piter.next().unwrap()[0] as i16;
|
||||||
c.y += 1;
|
|
||||||
}
|
|
||||||
c.x = 0;
|
|
||||||
c.y += piter.next().unwrap_or(&[1])[0] as i16;
|
|
||||||
}
|
}
|
||||||
'F' => {
|
'F' => {
|
||||||
let mut c = self.cursor.get_mut();
|
self.cursor.x = 0;
|
||||||
c.x = 0;
|
self.cursor.y -= piter.next().unwrap()[0] as i16;
|
||||||
c.y -= piter.next().unwrap_or(&[1])[0] as i16;
|
|
||||||
}
|
}
|
||||||
'G' | '`' => {
|
'G' => {
|
||||||
self.cursor.get_mut().x = piter.next().unwrap_or(&[1])[0] as i16 - 1;
|
self.cursor.x = piter.next().unwrap()[0] as i16 - 1;
|
||||||
}
|
}
|
||||||
'H' | 'f' => {
|
|
||||||
let mut c = self.cursor.get_mut();
|
's' => {
|
||||||
c.y = piter.next().unwrap_or(&[1])[0] as i16 - 1;
|
self.cursor_stack.push(self.cursor);
|
||||||
c.x = piter.next().unwrap_or(&[1])[0] as i16 - 1;
|
|
||||||
}
|
}
|
||||||
|
'u' => {
|
||||||
|
if let Some(c) = self.cursor_stack.pop() {
|
||||||
|
self.cursor = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
'J' => {
|
'J' => {
|
||||||
let x = piter.next().unwrap_or(&[0 as u16; 1]);
|
let x = piter.next().unwrap_or(&[0 as u16; 1]);
|
||||||
match x[0] {
|
match x[0] {
|
||||||
|
|
||||||
// clear from cursor until end of screen
|
// clear from cursor until end of screen
|
||||||
0 => {
|
0 => {
|
||||||
let mut pos = self.cursor.get();
|
let mut pos = self.cursor;
|
||||||
|
|
||||||
while pos.y < 100 {
|
while pos.y < 100 {
|
||||||
self.write_atom(pos, None);
|
self.write_atom(pos, None);
|
||||||
pos.x += 1;
|
pos.x += 1;
|
||||||
|
|
||||||
if pos.x >= self.size.get().x {
|
if pos.x >= self.term_width {
|
||||||
pos.x = 0;
|
pos.x = 0;
|
||||||
pos.y += 1;
|
pos.y += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
// clear from cursor to begin
|
// clear from cursor to begin
|
||||||
1 => {
|
1 => {
|
||||||
let mut pos = self.cursor.get();
|
let mut pos = self.cursor;
|
||||||
while pos.y >= 0 || pos.x >= 0 {
|
while pos.y >= 0 || pos.x >= 0 {
|
||||||
self.write_atom(pos, None);
|
self.write_atom(pos, None);
|
||||||
|
|
||||||
pos.x -= 1;
|
pos.x -= 1;
|
||||||
if pos.x < 0 {
|
if pos.x < 0 {
|
||||||
pos.x = self.size.get().x;
|
pos.x = self.term_width;
|
||||||
pos.y -= 1;
|
pos.y -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//self.cursor.x = 0;
|
//self.cursor.
|
||||||
}
|
}
|
||||||
|
|
||||||
// erase entire screen
|
// erase entire screen
|
||||||
2 => {
|
2 => {
|
||||||
for y in 0..100 {
|
for y in 0 .. 100 {
|
||||||
for x in 0..self.size.get().x {
|
for x in 0 .. self.term_width {
|
||||||
self.write_atom(Point2::new(x, y), None);
|
self.write_atom(Point2::new(x, y), None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.cursor.set(Point2::new(0, 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// invalid
|
// invalid
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'K' => {
|
|
||||||
let x = piter.next().unwrap_or(&[0]);
|
|
||||||
match x[0] {
|
|
||||||
// clear from cursor until end of line
|
|
||||||
0 => {
|
|
||||||
let c = self.cursor.get();
|
|
||||||
for x in c.x..self.size.get().x {
|
|
||||||
self.write_atom(
|
|
||||||
Point2::new(x, c.y),
|
|
||||||
Some(TerminalAtom::new(' ', self.get_style())),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// clear from start of line until cursor
|
'K' => {
|
||||||
1 => {
|
let x = piter.next().unwrap();
|
||||||
let c = self.cursor.get();
|
match x[0] {
|
||||||
for x in 0..c.x {
|
|
||||||
self.write_atom(
|
// clear cursor until end
|
||||||
Point2::new(x, c.y),
|
0 => {
|
||||||
Some(TerminalAtom::new(' ', self.get_style())),
|
for x in self.cursor.x .. self.term_width {
|
||||||
);
|
self.write_atom(Point2::new(x, self.cursor.y), None);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
|
// clear start until cursor
|
||||||
|
1 => {
|
||||||
|
for x in 0 .. self.cursor.x {
|
||||||
|
self.write_atom(Point2::new(x, self.cursor.y), None);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// clear entire line
|
// clear entire line
|
||||||
2 => {
|
2 => {
|
||||||
let c = self.cursor.get();
|
for x in 0 .. self.term_width {
|
||||||
for x in 0..self.size.get().x {
|
self.write_atom(Point2::new(x, self.cursor.y), None);
|
||||||
self.write_atom(
|
|
||||||
Point2::new(x, c.y),
|
|
||||||
Some(TerminalAtom::new(' ', self.get_style())),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
// invalid
|
// invalid
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'L' => {
|
|
||||||
self.insert_blank_lines(piter.next().unwrap_or(&[1])[0] as usize);
|
|
||||||
}
|
|
||||||
|
|
||||||
'M' => {
|
_ => {}
|
||||||
//eprintln!("delete lines");
|
|
||||||
/*
|
|
||||||
let n = piter.next().unwrap_or(&[1])[0] as i16;
|
|
||||||
for y in 0 .. n {
|
|
||||||
for x in 0 .. self.size.get().x {
|
|
||||||
self.write_atom(Point2::new(x, self.cursor.y+y), None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
'P' => {
|
|
||||||
for x in 0 .. piter.next().unwrap_or(&[1])[0] {
|
|
||||||
self.backspace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
'X' => {
|
|
||||||
//eprintln!("delete chars");
|
|
||||||
let c = self.cursor.get();
|
|
||||||
for x in 0..piter.next().unwrap_or(&[1])[0] {
|
|
||||||
self.write_atom(
|
|
||||||
Point2::new(c.x + x as i16, c.y),
|
|
||||||
Some(TerminalAtom::new(' ', self.get_style())),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
'S' => {
|
|
||||||
self.scroll_up(piter.next().unwrap_or(&[1])[0] as usize);
|
|
||||||
}
|
|
||||||
'T' => {
|
|
||||||
self.scroll_dn(piter.next().unwrap_or(&[1])[0] as usize);
|
|
||||||
}
|
|
||||||
's' => {
|
|
||||||
self.save_cursor_position();
|
|
||||||
}
|
|
||||||
'u' => {
|
|
||||||
self.restore_cursor_position();
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
/*
|
|
||||||
eprintln!(
|
|
||||||
"[csi_dispatch] params={:#?}, intermediates={:?}, ignore={:?}, char={:?}",
|
|
||||||
params, intermediates, ignore, c
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn esc_dispatch(&mut self, intermediates: &[u8], _ignore: bool, byte: u8) {
|
fn esc_dispatch(&mut self, intermediates: &[u8], ignore: bool, byte: u8) {
|
||||||
match (byte, intermediates) {
|
/*
|
||||||
//(b'B', intermediates) => configure_charset!(StandardCharset::Ascii, intermediates),
|
eprintln!(
|
||||||
(b'D', []) => self.linefeed(),
|
"[esc_dispatch] intermediates={:?}, ignore={:?}, byte={:02x}",
|
||||||
(b'E', []) => {
|
intermediates, ignore, byte
|
||||||
self.linefeed();
|
);
|
||||||
self.carriage_return();
|
*/
|
||||||
}
|
|
||||||
/*
|
match byte {
|
||||||
(b'H', []) => self.handler.set_horizontal_tabstop(),
|
|
||||||
(b'M', []) => self.handler.reverse_index(),
|
|
||||||
(b'Z', []) => self.handler.identify_terminal(None),
|
|
||||||
(b'c', []) => self.handler.reset_state(),
|
_ => {}
|
||||||
(b'0', intermediates) => {
|
|
||||||
configure_charset!(StandardCharset::SpecialCharacterAndLineDrawing, intermediates)
|
|
||||||
},
|
|
||||||
*/
|
|
||||||
(b'7', []) => self.save_cursor_position(),
|
|
||||||
//(b'8', [b'#']) => self.handler.decaln(),
|
|
||||||
(b'8', []) => self.restore_cursor_position(),
|
|
||||||
/*
|
|
||||||
(b'=', []) => self.handler.set_keypad_application_mode(),
|
|
||||||
(b'>', []) => self.handler.unset_keypad_application_mode(),
|
|
||||||
**/
|
|
||||||
// String terminator, do nothing (parser handles as string terminator).
|
|
||||||
(b'\\', []) => (),
|
|
||||||
_ => {
|
|
||||||
/*
|
|
||||||
eprintln!(
|
|
||||||
"unhandled esc_dispatch intermediates={:?}, ignore={:?}, byte={:02x}",
|
|
||||||
intermediates, ignore, byte
|
|
||||||
);
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use {
|
use {
|
||||||
super::TerminalStyle,
|
super::TerminalStyle,
|
||||||
serde::{Deserialize, Serialize},
|
serde::{Serialize, Deserialize}
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
|
#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
|
||||||
pub struct TerminalAtom {
|
pub struct TerminalAtom {
|
||||||
pub c: Option<char>,
|
pub c: Option<char>,
|
||||||
pub style: TerminalStyle,
|
pub style: TerminalStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TerminalAtom {
|
impl TerminalAtom {
|
||||||
|
@ -15,10 +15,7 @@ impl TerminalAtom {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_bg(bg_color: (u8, u8, u8)) -> Self {
|
pub fn new_bg(bg_color: (u8, u8, u8)) -> Self {
|
||||||
TerminalAtom {
|
TerminalAtom { c: None, style: TerminalStyle::bg_color(bg_color) }
|
||||||
c: None,
|
|
||||||
style: TerminalStyle::bg_color(bg_color),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_style_front(mut self, style: TerminalStyle) -> Self {
|
pub fn add_style_front(mut self, style: TerminalStyle) -> Self {
|
||||||
|
@ -36,7 +33,7 @@ impl From<char> for TerminalAtom {
|
||||||
fn from(c: char) -> Self {
|
fn from(c: char) -> Self {
|
||||||
TerminalAtom {
|
TerminalAtom {
|
||||||
c: Some(c),
|
c: Some(c),
|
||||||
style: TerminalStyle::default(),
|
style: TerminalStyle::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +42,7 @@ impl From<Option<char>> for TerminalAtom {
|
||||||
fn from(c: Option<char>) -> Self {
|
fn from(c: Option<char>) -> Self {
|
||||||
TerminalAtom {
|
TerminalAtom {
|
||||||
c,
|
c,
|
||||||
style: TerminalStyle::default(),
|
style: TerminalStyle::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +51,8 @@ impl From<&char> for TerminalAtom {
|
||||||
fn from(c: &char) -> Self {
|
fn from(c: &char) -> Self {
|
||||||
TerminalAtom {
|
TerminalAtom {
|
||||||
c: Some(*c),
|
c: Some(*c),
|
||||||
style: TerminalStyle::default(),
|
style: TerminalStyle::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
use {
|
use {
|
||||||
r3vi::{
|
std::{
|
||||||
view::{
|
sync::{Arc}
|
||||||
InnerViewPort, Observer, ObserverBroadcast, OuterViewPort, View,
|
|
||||||
index::*,
|
|
||||||
},
|
|
||||||
projection::projection_helper::*,
|
|
||||||
},
|
},
|
||||||
crate::{terminal::{TerminalAtom, TerminalView}},
|
|
||||||
cgmath::Point2,
|
|
||||||
std::sync::Arc,
|
|
||||||
std::sync::RwLock,
|
std::sync::RwLock,
|
||||||
|
cgmath::Point2,
|
||||||
|
crate::{
|
||||||
|
core::{InnerViewPort, OuterViewPort, View, Observer, ObserverBroadcast},
|
||||||
|
index::{IndexArea, IndexView},
|
||||||
|
terminal::{TerminalAtom, TerminalView},
|
||||||
|
projection::ProjectionHelper
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
@ -17,18 +17,22 @@ use {
|
||||||
pub struct TerminalCompositor {
|
pub struct TerminalCompositor {
|
||||||
layers: Vec<Arc<dyn TerminalView>>,
|
layers: Vec<Arc<dyn TerminalView>>,
|
||||||
cast: Arc<RwLock<ObserverBroadcast<dyn TerminalView>>>,
|
cast: Arc<RwLock<ObserverBroadcast<dyn TerminalView>>>,
|
||||||
proj_helper: ProjectionHelper<usize, Self>,
|
proj_helper: ProjectionHelper<usize, Self>
|
||||||
}
|
}
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
impl TerminalCompositor {
|
impl TerminalCompositor {
|
||||||
pub fn new(port: InnerViewPort<dyn TerminalView>) -> Arc<RwLock<Self>> {
|
pub fn new(
|
||||||
let comp = Arc::new(RwLock::new(TerminalCompositor {
|
port: InnerViewPort<dyn TerminalView>
|
||||||
layers: Vec::new(),
|
) -> Arc<RwLock<Self>> {
|
||||||
cast: port.get_broadcast(),
|
let comp = Arc::new(RwLock::new(
|
||||||
proj_helper: ProjectionHelper::new(port.0.update_hooks.clone()),
|
TerminalCompositor {
|
||||||
}));
|
layers: Vec::new(),
|
||||||
|
cast: port.get_broadcast(),
|
||||||
|
proj_helper: ProjectionHelper::new(port.0.update_hooks.clone())
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
comp.write().unwrap().proj_helper.set_proj(&comp);
|
comp.write().unwrap().proj_helper.set_proj(&comp);
|
||||||
port.set_view(Some(comp.clone()));
|
port.set_view(Some(comp.clone()));
|
||||||
|
@ -39,10 +43,13 @@ impl TerminalCompositor {
|
||||||
pub fn push(&mut self, v: OuterViewPort<dyn TerminalView>) {
|
pub fn push(&mut self, v: OuterViewPort<dyn TerminalView>) {
|
||||||
let idx = self.layers.len();
|
let idx = self.layers.len();
|
||||||
self.layers.push(
|
self.layers.push(
|
||||||
self.proj_helper
|
self.proj_helper.new_index_arg(
|
||||||
.new_index_arg(idx, v, |s: &mut Self, area| {
|
idx,
|
||||||
|
v,
|
||||||
|
|s: &mut Self, area| {
|
||||||
s.cast.notify(area);
|
s.cast.notify(area);
|
||||||
}),
|
}
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,3 +87,4 @@ impl IndexView<Point2<i16>> for TerminalCompositor {
|
||||||
area
|
area
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,21 @@
|
||||||
pub mod ansi_parser;
|
|
||||||
pub mod atom;
|
|
||||||
pub mod compositor;
|
|
||||||
pub mod style;
|
pub mod style;
|
||||||
|
pub mod atom;
|
||||||
pub mod terminal;
|
pub mod terminal;
|
||||||
pub mod widgets;
|
pub mod compositor;
|
||||||
|
pub mod ansi_parser;
|
||||||
|
|
||||||
pub use {
|
pub use {
|
||||||
atom::TerminalAtom,
|
style::{TerminalStyle},
|
||||||
compositor::TerminalCompositor,
|
atom::{TerminalAtom},
|
||||||
style::TerminalStyle,
|
|
||||||
terminal::{Terminal, TerminalEvent},
|
terminal::{Terminal, TerminalEvent},
|
||||||
|
compositor::TerminalCompositor,
|
||||||
};
|
};
|
||||||
|
|
||||||
use r3vi::view::grid::*;
|
use {
|
||||||
|
crate::{
|
||||||
|
grid::GridView
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
@ -22,7 +25,7 @@ pub trait TerminalView = GridView<Item = TerminalAtom>;
|
||||||
|
|
||||||
pub enum TerminalEditorResult {
|
pub enum TerminalEditorResult {
|
||||||
Continue,
|
Continue,
|
||||||
Exit,
|
Exit
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait TerminalEditor {
|
pub trait TerminalEditor {
|
||||||
|
@ -33,48 +36,26 @@ pub trait TerminalEditor {
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
use {
|
use {
|
||||||
r3vi::{
|
crate::{
|
||||||
view::{OuterViewPort},
|
vec::VecBuffer,
|
||||||
buffer::vec::*,
|
core::{ViewPort, OuterViewPort}
|
||||||
},
|
},
|
||||||
cgmath::Point2,
|
cgmath::Point2
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn make_label(s: &str) -> OuterViewPort<dyn TerminalView> {
|
pub fn make_label(s: &str) -> OuterViewPort<dyn TerminalView> {
|
||||||
let label = VecBuffer::with_data(s.chars().collect());
|
let label_port = ViewPort::new();
|
||||||
|
let mut label = VecBuffer::with_data(s.chars().collect(), label_port.inner());
|
||||||
|
|
||||||
let v = label.get_port()
|
let v = label_port.outer()
|
||||||
.to_sequence()
|
.to_sequence()
|
||||||
.map(|c| TerminalAtom::from(c))
|
.map(|c| TerminalAtom::from(c))
|
||||||
.to_index()
|
.to_index()
|
||||||
.map_key(
|
.map_key(
|
||||||
|idx| Point2::new(*idx as i16, 0),
|
|idx| Point2::new(*idx as i16, 0),
|
||||||
|pt| if pt.y == 0 { Some(pt.x as usize) } else { None },
|
|pt| if pt.y == 0 { Some(pt.x as usize) } else { None }
|
||||||
);
|
);
|
||||||
|
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait TerminalProjections {
|
|
||||||
fn with_style(&self, style: TerminalStyle) -> OuterViewPort<dyn TerminalView>;
|
|
||||||
fn with_fg_color(&self, col: (u8, u8, u8)) -> OuterViewPort<dyn TerminalView>;
|
|
||||||
fn with_bg_color(&self, col: (u8, u8, u8)) -> OuterViewPort<dyn TerminalView>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TerminalProjections for OuterViewPort<dyn TerminalView> {
|
|
||||||
fn with_style(&self, style: TerminalStyle) -> OuterViewPort<dyn TerminalView> {
|
|
||||||
self.map_item(
|
|
||||||
move |_idx, a|
|
|
||||||
a.add_style_front(style)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_fg_color(&self, col: (u8, u8, u8)) -> OuterViewPort<dyn TerminalView> {
|
|
||||||
self.with_style(TerminalStyle::fg_color(col))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_bg_color(&self, col: (u8, u8, u8)) -> OuterViewPort<dyn TerminalView> {
|
|
||||||
self.with_style(TerminalStyle::bg_color(col))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
#[derive(Default, Copy, Clone, PartialEq, Serialize, Deserialize, Debug)]
|
#[derive(Default, Copy, Clone, PartialEq, Serialize, Deserialize, Debug)]
|
||||||
pub struct TerminalStyle {
|
pub struct TerminalStyle {
|
||||||
|
@ -6,7 +6,7 @@ pub struct TerminalStyle {
|
||||||
pub bg_color: Option<(u8, u8, u8)>,
|
pub bg_color: Option<(u8, u8, u8)>,
|
||||||
pub bold: Option<bool>,
|
pub bold: Option<bool>,
|
||||||
pub italic: Option<bool>,
|
pub italic: Option<bool>,
|
||||||
pub underline: Option<bool>,
|
pub underline: Option<bool>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TerminalStyle {
|
impl TerminalStyle {
|
||||||
|
@ -85,3 +85,4 @@ impl std::fmt::Display for TerminalStyle {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,33 +1,44 @@
|
||||||
use {
|
use {
|
||||||
r3vi::{
|
std::{
|
||||||
view::{
|
sync::Arc,
|
||||||
channel::{queue_channel, set_channel, ChannelReceiver, ChannelSender},
|
io::{Write, stdout, stdin},
|
||||||
Observer, OuterViewPort,
|
collections::HashSet
|
||||||
grid::*,
|
},
|
||||||
index::*,
|
std::sync::RwLock,
|
||||||
}
|
async_std::{
|
||||||
|
stream::StreamExt,
|
||||||
|
task
|
||||||
},
|
},
|
||||||
super::{TerminalStyle, TerminalView},
|
|
||||||
async_std::{stream::StreamExt, task},
|
|
||||||
cgmath::{Point2, Vector2},
|
|
||||||
signal_hook,
|
signal_hook,
|
||||||
signal_hook_async_std::Signals,
|
signal_hook_async_std::Signals,
|
||||||
std::sync::RwLock,
|
cgmath::{Vector2, Point2},
|
||||||
std::{
|
|
||||||
collections::HashSet,
|
|
||||||
io::{stdin, stdout, Write},
|
|
||||||
sync::Arc,
|
|
||||||
},
|
|
||||||
termion::{
|
termion::{
|
||||||
input::{MouseTerminal, TermRead},
|
|
||||||
raw::IntoRawMode,
|
raw::IntoRawMode,
|
||||||
|
input::{TermRead, MouseTerminal}
|
||||||
|
},
|
||||||
|
crate::{
|
||||||
|
core::{
|
||||||
|
OuterViewPort,
|
||||||
|
Observer,
|
||||||
|
channel::{
|
||||||
|
ChannelReceiver,
|
||||||
|
ChannelSender,
|
||||||
|
queue_channel,
|
||||||
|
set_channel
|
||||||
|
}
|
||||||
|
},
|
||||||
|
index::{IndexArea},
|
||||||
|
grid::{GridWindowIterator}
|
||||||
|
},
|
||||||
|
super::{
|
||||||
|
TerminalStyle,
|
||||||
|
TerminalView
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone)]
|
|
||||||
pub enum TerminalEvent {
|
pub enum TerminalEvent {
|
||||||
Resize(Vector2<i16>),
|
Resize(Vector2<i16>),
|
||||||
Input(termion::event::Event),
|
Input(termion::event::Event)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Terminal {
|
pub struct Terminal {
|
||||||
|
@ -35,22 +46,24 @@ pub struct Terminal {
|
||||||
_observer: Arc<RwLock<TermOutObserver>>,
|
_observer: Arc<RwLock<TermOutObserver>>,
|
||||||
|
|
||||||
events: ChannelReceiver<Vec<TerminalEvent>>,
|
events: ChannelReceiver<Vec<TerminalEvent>>,
|
||||||
_signal_handle: signal_hook_async_std::Handle,
|
_signal_handle: signal_hook_async_std::Handle
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Terminal {
|
impl Terminal {
|
||||||
pub fn new(port: OuterViewPort<dyn TerminalView>) -> Self {
|
pub fn new(
|
||||||
|
port: OuterViewPort<dyn TerminalView>
|
||||||
|
) -> Self {
|
||||||
let (dirty_pos_tx, dirty_pos_rx) = set_channel();
|
let (dirty_pos_tx, dirty_pos_rx) = set_channel();
|
||||||
|
|
||||||
let writer = Arc::new(TermOutWriter {
|
let writer = Arc::new(TermOutWriter {
|
||||||
out: RwLock::new(MouseTerminal::from(stdout().into_raw_mode().unwrap())),
|
out: RwLock::new(MouseTerminal::from(stdout().into_raw_mode().unwrap())),
|
||||||
dirty_pos_rx,
|
dirty_pos_rx,
|
||||||
view: port.get_view_arc(),
|
view: port.get_view_arc()
|
||||||
});
|
});
|
||||||
|
|
||||||
let observer = Arc::new(RwLock::new(TermOutObserver {
|
let observer = Arc::new(RwLock::new(TermOutObserver {
|
||||||
dirty_pos_tx,
|
dirty_pos_tx,
|
||||||
writer: writer.clone(),
|
writer: writer.clone()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
port.add_observer(observer.clone());
|
port.add_observer(observer.clone());
|
||||||
|
@ -69,7 +82,7 @@ impl Terminal {
|
||||||
event_tx.send(TerminalEvent::Resize(Vector2::new(w as i16, h as i16)));
|
event_tx.send(TerminalEvent::Resize(Vector2::new(w as i16, h as i16)));
|
||||||
|
|
||||||
// and again on SIGWINCH
|
// and again on SIGWINCH
|
||||||
let signals = Signals::new(&[signal_hook::consts::signal::SIGWINCH]).unwrap();
|
let signals = Signals::new(&[ signal_hook::consts::signal::SIGWINCH ]).unwrap();
|
||||||
let handle = signals.handle();
|
let handle = signals.handle();
|
||||||
|
|
||||||
task::spawn(async move {
|
task::spawn(async move {
|
||||||
|
@ -77,9 +90,9 @@ impl Terminal {
|
||||||
while let Some(signal) = signals.next().await {
|
while let Some(signal) = signals.next().await {
|
||||||
match signal {
|
match signal {
|
||||||
signal_hook::consts::signal::SIGWINCH => {
|
signal_hook::consts::signal::SIGWINCH => {
|
||||||
let (w, h) = termion::terminal_size().unwrap();
|
let (w,h) = termion::terminal_size().unwrap();
|
||||||
event_tx.send(TerminalEvent::Resize(Vector2::new(w as i16, h as i16)));
|
event_tx.send(TerminalEvent::Resize(Vector2::new(w as i16, h as i16)));
|
||||||
}
|
},
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,7 +102,7 @@ impl Terminal {
|
||||||
writer,
|
writer,
|
||||||
_observer: observer,
|
_observer: observer,
|
||||||
events: event_rx,
|
events: event_rx,
|
||||||
_signal_handle: handle,
|
_signal_handle: handle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,18 +117,16 @@ impl Terminal {
|
||||||
|
|
||||||
struct TermOutObserver {
|
struct TermOutObserver {
|
||||||
dirty_pos_tx: ChannelSender<HashSet<Point2<i16>>>,
|
dirty_pos_tx: ChannelSender<HashSet<Point2<i16>>>,
|
||||||
writer: Arc<TermOutWriter>,
|
writer: Arc<TermOutWriter>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TermOutObserver {
|
impl TermOutObserver {
|
||||||
fn send_area(&mut self, area: IndexArea<Point2<i16>>) {
|
fn send_area(&mut self, area: IndexArea<Point2<i16>>) {
|
||||||
match area {
|
match area {
|
||||||
IndexArea::Empty => {}
|
IndexArea::Empty => {},
|
||||||
IndexArea::Full => {
|
IndexArea::Full => {
|
||||||
let (w, h) = termion::terminal_size().unwrap();
|
let (w, h) = termion::terminal_size().unwrap();
|
||||||
for pos in
|
for pos in GridWindowIterator::from(Point2::new(0, 0) .. Point2::new(w as i16, h as i16)) {
|
||||||
GridWindowIterator::from(Point2::new(0, 0)..Point2::new(w as i16, h as i16))
|
|
||||||
{
|
|
||||||
self.dirty_pos_tx.send(pos);
|
self.dirty_pos_tx.send(pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,7 +160,7 @@ impl Observer<dyn TerminalView> for TermOutObserver {
|
||||||
pub struct TermOutWriter {
|
pub struct TermOutWriter {
|
||||||
out: RwLock<MouseTerminal<termion::raw::RawTerminal<std::io::Stdout>>>,
|
out: RwLock<MouseTerminal<termion::raw::RawTerminal<std::io::Stdout>>>,
|
||||||
dirty_pos_rx: ChannelReceiver<HashSet<Point2<i16>>>,
|
dirty_pos_rx: ChannelReceiver<HashSet<Point2<i16>>>,
|
||||||
view: Arc<RwLock<Option<Arc<dyn TerminalView>>>>,
|
view: Arc<RwLock<Option<Arc<dyn TerminalView>>>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TermOutWriter {
|
impl TermOutWriter {
|
||||||
|
@ -162,51 +173,41 @@ impl TermOutWriter {
|
||||||
impl TermOutWriter {
|
impl TermOutWriter {
|
||||||
pub async fn show(&self) -> std::io::Result<()> {
|
pub async fn show(&self) -> std::io::Result<()> {
|
||||||
// init
|
// init
|
||||||
write!(
|
write!(self.out.write().unwrap(), "{}{}{}",
|
||||||
self.out.write().unwrap(),
|
termion::cursor::Hide,
|
||||||
"{}{}{}",
|
termion::cursor::Goto(1, 1),
|
||||||
termion::cursor::Hide,
|
termion::style::Reset)?;
|
||||||
termion::cursor::Goto(1, 1),
|
|
||||||
termion::style::Reset
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let mut cur_pos = Point2::<i16>::new(0, 0);
|
let mut cur_pos = Point2::<i16>::new(0, 0);
|
||||||
let mut cur_style = TerminalStyle::default();
|
let mut cur_style = TerminalStyle::default();
|
||||||
|
|
||||||
// draw atoms until view port is destroyed
|
// draw atoms until view port is destroyed
|
||||||
while let Some(dirty_pos) = self.dirty_pos_rx.recv().await {
|
while let Some(dirty_pos) = self.dirty_pos_rx.recv().await {
|
||||||
let (w, _h) = termion::terminal_size().unwrap();
|
let (w, h) = termion::terminal_size().unwrap();
|
||||||
|
|
||||||
if let Some(view) = self.view.read().unwrap().as_ref() {
|
if let Some(view) = self.view.read().unwrap().as_ref() {
|
||||||
let mut out = self.out.write().unwrap();
|
let mut out = self.out.write().unwrap();
|
||||||
|
|
||||||
let d = dirty_pos
|
let d = dirty_pos.into_iter().filter(|p| p.x >= 0 && p.y >= 0 && p.x < w as i16 && p.y < w as i16);//.collect::<Vec<_>>();
|
||||||
.into_iter()
|
/*
|
||||||
.filter(|p| p.x >= 0 && p.y >= 0 && p.x < w as i16 && p.y < w as i16); //.collect::<Vec<_>>();
|
d.sort_by(|a,b| {
|
||||||
/*
|
if a.y < b.y {
|
||||||
d.sort_by(|a,b| {
|
std::cmp::Ordering::Less
|
||||||
if a.y < b.y {
|
} else if a.y == b.y {
|
||||||
std::cmp::Ordering::Less
|
a.x.cmp(&b.x)
|
||||||
} else if a.y == b.y {
|
} else {
|
||||||
a.x.cmp(&b.x)
|
std::cmp::Ordering::Greater
|
||||||
} else {
|
}
|
||||||
std::cmp::Ordering::Greater
|
});
|
||||||
}
|
*/
|
||||||
});
|
|
||||||
*/
|
|
||||||
for pos in d {
|
for pos in d {
|
||||||
if pos != cur_pos {
|
if pos != cur_pos {
|
||||||
write!(
|
write!(out, "{}", termion::cursor::Goto(pos.x as u16 + 1, pos.y as u16 + 1))?;
|
||||||
out,
|
|
||||||
"{}",
|
|
||||||
termion::cursor::Goto(pos.x as u16 + 1, pos.y as u16 + 1)
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(atom) = view.get(&pos) {
|
if let Some(atom) = view.get(&pos) {
|
||||||
if cur_style != atom.style {
|
if cur_style != atom.style {
|
||||||
cur_style = atom.style;
|
cur_style = atom.style;
|
||||||
write!(out, "{}", termion::style::Reset)?;
|
|
||||||
write!(out, "{}", atom.style)?;
|
write!(out, "{}", atom.style)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,3 +232,4 @@ impl TermOutWriter {
|
||||||
std::io::Result::Ok(())
|
std::io::Result::Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
|
|
||||||
pub mod ascii_box;
|
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
use {
|
|
||||||
cgmath::Point2,
|
|
||||||
nested::{
|
|
||||||
core::{InnerViewPort, Observer, ObserverBroadcast, OuterViewPort, View},
|
|
||||||
sequence::{SequenceView},
|
|
||||||
index::{IndexArea, IndexView},
|
|
||||||
projection::ProjectionHelper,
|
|
||||||
terminal::{TerminalAtom, TerminalView},
|
|
||||||
},
|
|
||||||
std::sync::{Arc, RwLock},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Plot {
|
|
||||||
limit: usize,
|
|
||||||
data: Arc<dyn SequenceView<Item = usize>>,
|
|
||||||
cast: Arc<RwLock<ObserverBroadcast<dyn TerminalView>>>,
|
|
||||||
proj_helper: ProjectionHelper<(), Self>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl View for Plot {
|
|
||||||
type Msg = IndexArea<Point2<i16>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IndexView<Point2<i16>> for Plot {
|
|
||||||
type Item = TerminalAtom;
|
|
||||||
|
|
||||||
fn get(&self, pt: &Point2<i16>) -> Option<TerminalAtom> {
|
|
||||||
if pt.y >= 0 {
|
|
||||||
if let Some(cur_val) = self.data.get(&(pt.x as usize)) {
|
|
||||||
if cur_val <= self.limit {
|
|
||||||
if pt.y == (self.limit - cur_val) as i16 {
|
|
||||||
return Some(TerminalAtom::from(if cur_val < 4 {
|
|
||||||
'o'
|
|
||||||
} else if cur_val < 8 {
|
|
||||||
'O'
|
|
||||||
} else {
|
|
||||||
'*'
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if pt.x > 0 {
|
|
||||||
if let Some(prev_val) = self.data.get(&((pt.x - 1) as usize)) {
|
|
||||||
if (pt.y > (self.limit - prev_val) as i16
|
|
||||||
&& pt.y < (self.limit - cur_val) as i16)
|
|
||||||
|| (pt.y < (self.limit - prev_val) as i16
|
|
||||||
&& pt.y > (self.limit - cur_val) as i16)
|
|
||||||
{
|
|
||||||
return Some(TerminalAtom::from('.'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn area(&self) -> IndexArea<Point2<i16>> {
|
|
||||||
IndexArea::Range(
|
|
||||||
Point2::new(0, 0)..=Point2::new(self.data.len().unwrap_or(0) as i16, self.limit as i16),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Plot {
|
|
||||||
pub fn new(
|
|
||||||
data_port: OuterViewPort<dyn SequenceView<Item = usize>>,
|
|
||||||
out_port: InnerViewPort<dyn TerminalView>,
|
|
||||||
) -> Arc<RwLock<Self>> {
|
|
||||||
let mut proj_helper = ProjectionHelper::new(out_port.0.update_hooks.clone());
|
|
||||||
let proj = Arc::new(RwLock::new(Plot {
|
|
||||||
data: proj_helper.new_sequence_arg((), data_port, |s: &mut Self, idx| {
|
|
||||||
let val = s.data.get(idx).unwrap_or(0);
|
|
||||||
|
|
||||||
if val > s.limit {
|
|
||||||
s.limit = val;
|
|
||||||
s.cast.notify(&s.area());
|
|
||||||
} else {
|
|
||||||
s.cast.notify(&IndexArea::Range(
|
|
||||||
Point2::new(*idx as i16, 0)..=Point2::new(*idx as i16, s.limit as i16),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
limit: 0,
|
|
||||||
cast: out_port.get_broadcast(),
|
|
||||||
proj_helper,
|
|
||||||
}));
|
|
||||||
|
|
||||||
proj.write().unwrap().proj_helper.set_proj(&proj);
|
|
||||||
out_port.set_view(Some(proj.clone()));
|
|
||||||
|
|
||||||
proj
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
|
|
||||||
pub struct TreeAddr(pub Vec<usize>);
|
|
||||||
|
|
||||||
impl From<Vec<usize>> for TreeAddr {
|
|
||||||
fn from(v: Vec<usize>) -> TreeAddr {
|
|
||||||
TreeAddr(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
use {
|
|
||||||
crate::editors::list::ListCursorMode
|
|
||||||
};
|
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
|
||||||
pub struct TreeCursor {
|
|
||||||
pub leaf_mode: ListCursorMode,
|
|
||||||
pub tree_addr: Vec<isize>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TreeCursor {
|
|
||||||
pub fn home() -> Self {
|
|
||||||
TreeCursor {
|
|
||||||
leaf_mode: ListCursorMode::Insert,
|
|
||||||
tree_addr: vec![0],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn none() -> Self {
|
|
||||||
TreeCursor {
|
|
||||||
leaf_mode: ListCursorMode::Select,
|
|
||||||
tree_addr: vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_subcursor(&self, depth: usize) -> TreeCursor {
|
|
||||||
TreeCursor {
|
|
||||||
leaf_mode: self.leaf_mode,
|
|
||||||
tree_addr: if depth < self.tree_addr.len() {
|
|
||||||
self.tree_addr[ depth.. ].iter().cloned().collect()
|
|
||||||
} else {
|
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for TreeCursor {
|
|
||||||
fn default() -> Self {
|
|
||||||
TreeCursor::none()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
pub mod addr;
|
|
||||||
pub mod cursor;
|
|
||||||
pub mod nav;
|
|
||||||
pub mod node;
|
|
||||||
pub mod treetype;
|
|
||||||
|
|
||||||
pub use {
|
|
||||||
addr::TreeAddr,
|
|
||||||
cursor::TreeCursor,
|
|
||||||
nav::{TreeNav, TreeNavResult, TreeHeightOp},
|
|
||||||
treetype::{TreeType},
|
|
||||||
node::NestedNode
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,184 +0,0 @@
|
||||||
use {
|
|
||||||
r3vi::{
|
|
||||||
view::{
|
|
||||||
OuterViewPort,
|
|
||||||
singleton::*,
|
|
||||||
sequence::*,
|
|
||||||
},
|
|
||||||
buffer::{
|
|
||||||
singleton::SingletonBuffer,
|
|
||||||
vec::VecBuffer
|
|
||||||
},
|
|
||||||
projection::{
|
|
||||||
decorate_sequence::*,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
crate::{
|
|
||||||
editors::list::ListCursorMode,
|
|
||||||
tree::TreeCursor,
|
|
||||||
terminal::{TerminalView, TerminalProjections, make_label}
|
|
||||||
},
|
|
||||||
cgmath::Vector2,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
|
|
||||||
pub enum TreeNavResult { Continue, Exit }
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
|
|
||||||
pub enum TreeHeightOp { P, Q, Max }
|
|
||||||
|
|
||||||
pub trait TreeNav {
|
|
||||||
/* CORE
|
|
||||||
*/
|
|
||||||
fn get_cursor(&self) -> TreeCursor {
|
|
||||||
TreeCursor::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_addr_view(&self) -> OuterViewPort<dyn SequenceView<Item = isize>> {
|
|
||||||
VecBuffer::<isize>::new().get_port().to_sequence()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_mode_view(&self) -> OuterViewPort<dyn SingletonView<Item = ListCursorMode>> {
|
|
||||||
SingletonBuffer::new(ListCursorMode::Select).get_port()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_cursor_warp(&self) -> TreeCursor {
|
|
||||||
TreeCursor::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_height(&self, _op: &TreeHeightOp) -> usize {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn goby(&mut self, _direction: Vector2<isize>) -> TreeNavResult {
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
|
|
||||||
fn goto(&mut self, _new_cursor: TreeCursor) -> TreeNavResult {
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
|
|
||||||
/* HULL
|
|
||||||
*/
|
|
||||||
fn set_addr(&mut self, addr: isize) -> TreeNavResult {
|
|
||||||
let mut c = self.get_cursor();
|
|
||||||
c.tree_addr[0] = addr;
|
|
||||||
self.goto(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_leaf_mode(&mut self, new_leaf_mode: ListCursorMode) -> TreeNavResult {
|
|
||||||
let mut c = self.get_cursor();
|
|
||||||
c.leaf_mode = new_leaf_mode;
|
|
||||||
self.goto(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_leaf_mode(&mut self) -> ListCursorMode {
|
|
||||||
self.get_cursor().leaf_mode
|
|
||||||
}
|
|
||||||
|
|
||||||
fn toggle_leaf_mode(&mut self) -> TreeNavResult {
|
|
||||||
let old_mode = self.get_leaf_mode();
|
|
||||||
self.set_leaf_mode(
|
|
||||||
match old_mode {
|
|
||||||
ListCursorMode::Insert => ListCursorMode::Select,
|
|
||||||
ListCursorMode::Select => ListCursorMode::Insert
|
|
||||||
}
|
|
||||||
);
|
|
||||||
TreeNavResult::Continue
|
|
||||||
}
|
|
||||||
|
|
||||||
fn up(&mut self) -> TreeNavResult {
|
|
||||||
self.goby(Vector2::new(0, -1))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dn(&mut self) -> TreeNavResult {
|
|
||||||
self.goby(Vector2::new(0, 1))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pxev(&mut self) -> TreeNavResult {
|
|
||||||
self.goby(Vector2::new(-1, 0))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn nexd(&mut self) -> TreeNavResult {
|
|
||||||
self.goby(Vector2::new(1, 0))
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
fn qpxev(&mut self) -> TreeNavResult {
|
|
||||||
let mut c = self.get_cursor();
|
|
||||||
match c.tree_addr.len() {
|
|
||||||
0 => {
|
|
||||||
self.goto(TreeCursor::home())
|
|
||||||
},
|
|
||||||
depth => {
|
|
||||||
if c.tree_addr[depth-1] != 0 {
|
|
||||||
c.tree_addr[depth-1] = 0;
|
|
||||||
} else {
|
|
||||||
self.pxev();
|
|
||||||
c = self.get_cursor();
|
|
||||||
let d = c.tree_addr.len();
|
|
||||||
if d > 0 {
|
|
||||||
c.tree_addr[d-1] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.goto(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn qnexd(&mut self) -> TreeNavResult {
|
|
||||||
let mut c = self.get_cursor_warp();
|
|
||||||
match c.tree_addr.len() {
|
|
||||||
0 => {
|
|
||||||
TreeNavResult::Exit
|
|
||||||
},
|
|
||||||
depth => {
|
|
||||||
if c.tree_addr[depth-1] != -1 {
|
|
||||||
c.tree_addr[depth-1] = -1;
|
|
||||||
} else {
|
|
||||||
self.nexd();
|
|
||||||
c = self.get_cursor();
|
|
||||||
let d = c.tree_addr.len();
|
|
||||||
if d > 0 {
|
|
||||||
c.tree_addr[d-1] = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.goto(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_cursor_widget(&self) -> OuterViewPort<dyn TerminalView> {
|
|
||||||
VecBuffer::with_data(
|
|
||||||
vec![
|
|
||||||
make_label("@").with_fg_color((150, 80,230)),
|
|
||||||
self.get_addr_view()
|
|
||||||
.map(|i|
|
|
||||||
make_label(&format!("{}", i)).with_fg_color((0, 100, 20)))
|
|
||||||
.separate(make_label(".").with_fg_color((150, 80,230)))
|
|
||||||
.to_grid_horizontal()
|
|
||||||
.flatten(),
|
|
||||||
make_label(":").with_fg_color((150, 80,230)),
|
|
||||||
self.get_mode_view()
|
|
||||||
.map(|mode| {
|
|
||||||
make_label(
|
|
||||||
match mode {
|
|
||||||
ListCursorMode::Insert => "INSERT",
|
|
||||||
ListCursorMode::Select => "SELECT"
|
|
||||||
})
|
|
||||||
.with_fg_color((200, 200, 20))
|
|
||||||
})
|
|
||||||
.to_grid()
|
|
||||||
.flatten(),
|
|
||||||
make_label(":").with_fg_color((150, 80,230))
|
|
||||||
]
|
|
||||||
).get_port()
|
|
||||||
.to_sequence()
|
|
||||||
.to_grid_horizontal()
|
|
||||||
.flatten()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
|
@ -1,343 +0,0 @@
|
||||||
use {
|
|
||||||
std::{sync::{Arc, RwLock}, any::Any},
|
|
||||||
cgmath::{Vector2, Point2},
|
|
||||||
r3vi::{
|
|
||||||
view::{View, ViewPort, OuterViewPort, AnyOuterViewPort, singleton::*, sequence::*},
|
|
||||||
buffer::{singleton::*}
|
|
||||||
},
|
|
||||||
laddertypes::{TypeTerm},
|
|
||||||
crate::{
|
|
||||||
type_system::{ReprTree, Context},
|
|
||||||
terminal::{TerminalView, TerminalEvent, TerminalEditor, TerminalEditorResult, TerminalAtom},
|
|
||||||
diagnostics::{Diagnostics, Message},
|
|
||||||
tree::{TreeNav, TreeCursor, TreeNavResult, TreeHeightOp},
|
|
||||||
editors::list::{ListCursorMode},
|
|
||||||
commander::ObjCommander,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* TODO: refactoring proposal
|
|
||||||
|
|
||||||
struct NestedNodeDisplay {
|
|
||||||
/// display view
|
|
||||||
pub view: Option< OuterViewPort<dyn TerminalView> >,
|
|
||||||
|
|
||||||
/// diagnostics
|
|
||||||
pub diag: Option< OuterViewPort<dyn SequenceView<Item = Message>> >,
|
|
||||||
|
|
||||||
/// depth
|
|
||||||
pub depth: SingletonBuffer< usize >,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct NestedNodeEdit {
|
|
||||||
/// abstract editor
|
|
||||||
pub editor: SingletonBuffer<
|
|
||||||
Option< Arc<dyn Any + Send + Sync> >
|
|
||||||
>,
|
|
||||||
|
|
||||||
pub spillbuf: VecBuffer< NestedNode >,
|
|
||||||
|
|
||||||
/// commander & navigation
|
|
||||||
pub cmd: SingletonBuffer<
|
|
||||||
Option< Arc<RwLock<dyn ObjCommander + Send + Sync>> >
|
|
||||||
>,
|
|
||||||
pub close_char: SingletonBuffer< Option< char > >,
|
|
||||||
|
|
||||||
// could be replaced by cmd when TreeNav -CmdObjects are used
|
|
||||||
pub tree_nav: SingletonBuffer<
|
|
||||||
Option< Arc<RwLock<dyn TreeNav + Send + Sync>> >
|
|
||||||
>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct NewNestedNode {
|
|
||||||
/// context
|
|
||||||
pub ctx: Arc<RwLock<Context>>,
|
|
||||||
|
|
||||||
/// abstract data view
|
|
||||||
pub data: Arc<RwLock<ReprTree>>,
|
|
||||||
|
|
||||||
/// viewports for terminal display
|
|
||||||
pub disp: NestedNodeDisplay,
|
|
||||||
|
|
||||||
/// editor & commander objects
|
|
||||||
pub edit: NestedNodeEdit
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct NestedNode {
|
|
||||||
/// context
|
|
||||||
pub ctx: Arc<RwLock<Context>>,
|
|
||||||
|
|
||||||
/// abstract data view
|
|
||||||
pub data: Arc<RwLock<ReprTree>>,
|
|
||||||
|
|
||||||
/// display view
|
|
||||||
pub view: Option< OuterViewPort<dyn TerminalView> >,
|
|
||||||
|
|
||||||
/// diagnostics
|
|
||||||
pub diag: Option< OuterViewPort<dyn SequenceView<Item = Message>> >,
|
|
||||||
|
|
||||||
/// depth
|
|
||||||
pub depth: OuterViewPort< dyn SingletonView<Item = usize> >,
|
|
||||||
|
|
||||||
/// abstract editor
|
|
||||||
pub editor: SingletonBuffer<
|
|
||||||
Option< Arc<dyn Any + Send + Sync> >
|
|
||||||
>,
|
|
||||||
|
|
||||||
pub spillbuf: Arc<RwLock<Vec<Arc<RwLock<NestedNode>>>>>,
|
|
||||||
|
|
||||||
/// commander & navigation
|
|
||||||
pub cmd: SingletonBuffer<
|
|
||||||
Option< Arc<RwLock<dyn ObjCommander + Send + Sync>> >
|
|
||||||
>,
|
|
||||||
pub close_char: SingletonBuffer<
|
|
||||||
Option< char >
|
|
||||||
>,
|
|
||||||
pub tree_nav: SingletonBuffer<
|
|
||||||
Option< Arc<RwLock<dyn TreeNav + Send + Sync>> >
|
|
||||||
>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NestedNode {
|
|
||||||
pub fn new(ctx: Arc<RwLock<Context>>, data: Arc<RwLock<ReprTree>>, depth: OuterViewPort<dyn SingletonView<Item = usize>>) -> Self {
|
|
||||||
NestedNode {
|
|
||||||
ctx,
|
|
||||||
data,
|
|
||||||
view: None,
|
|
||||||
diag: None,
|
|
||||||
depth,
|
|
||||||
editor: SingletonBuffer::new(None),
|
|
||||||
spillbuf: Arc::new(RwLock::new(Vec::new())),
|
|
||||||
cmd: SingletonBuffer::new(None),
|
|
||||||
close_char: SingletonBuffer::new(None),
|
|
||||||
tree_nav: SingletonBuffer::new(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: move into separate file/module
|
|
||||||
*/
|
|
||||||
pub fn from_char(ctx: Arc<RwLock<Context>>, c: char) -> NestedNode {
|
|
||||||
let buf = r3vi::buffer::singleton::SingletonBuffer::<char>::new(c);
|
|
||||||
|
|
||||||
NestedNode::new(
|
|
||||||
ctx.clone(),
|
|
||||||
ReprTree::new_leaf(
|
|
||||||
Context::parse(&ctx, "Char"),
|
|
||||||
buf.get_port().into()
|
|
||||||
),
|
|
||||||
SingletonBuffer::new(0).get_port()
|
|
||||||
)
|
|
||||||
.set_view(buf.get_port()
|
|
||||||
.map(|c| TerminalAtom::from(c))
|
|
||||||
.to_index()
|
|
||||||
.map_key(
|
|
||||||
|_x| {
|
|
||||||
Point2::new(0, 0)
|
|
||||||
},
|
|
||||||
|p| {
|
|
||||||
if *p == Point2::new(0,0) { Some(()) } else { None }
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.set_editor(Arc::new(RwLock::new(buf)))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//\\//\\
|
|
||||||
|
|
||||||
pub fn morph(self, ty: TypeTerm) -> NestedNode {
|
|
||||||
Context::morph_node(self, ty)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_type(&self) -> TypeTerm {
|
|
||||||
self.data.read().unwrap().get_type().clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
//\\//\\
|
|
||||||
|
|
||||||
pub fn set_editor(mut self, editor: Arc<dyn Any + Send + Sync>) -> Self {
|
|
||||||
self.editor.set(Some(editor));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_view(mut self, view: OuterViewPort<dyn TerminalView>) -> Self {
|
|
||||||
self.view = Some(view);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_cmd(mut self, cmd: Arc<RwLock<dyn ObjCommander + Send + Sync>>) -> Self {
|
|
||||||
self.cmd.set(Some(cmd));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_nav(mut self, nav: Arc<RwLock<dyn TreeNav + Send + Sync>>) -> Self {
|
|
||||||
self.tree_nav.set(Some(nav));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_diag(mut self, diag: OuterViewPort<dyn SequenceView<Item = Message>>) -> Self {
|
|
||||||
self.diag = Some(diag);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
//\\//\\
|
|
||||||
|
|
||||||
pub fn get_diag(&self) -> OuterViewPort<dyn SequenceView<Item = Message>> {
|
|
||||||
self.diag.clone().unwrap_or(ViewPort::new().into_outer())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_view(&self) -> OuterViewPort<dyn TerminalView> {
|
|
||||||
self.view.clone().unwrap_or(ViewPort::new().into_outer())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_data_port<'a, V: View + ?Sized + 'static>(&'a self, type_str: impl Iterator<Item = &'a str>) -> Option<OuterViewPort<V>>
|
|
||||||
where V::Msg: Clone {
|
|
||||||
let ctx = self.ctx.clone();
|
|
||||||
let type_ladder = type_str.map(|s| Context::parse(&ctx, s));
|
|
||||||
|
|
||||||
let repr_tree = ReprTree::descend_ladder(&self.data, type_ladder)?;
|
|
||||||
repr_tree.clone().read().unwrap()
|
|
||||||
.get_port::<V>().clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_data_view<'a, V: View + ?Sized + 'static>(&'a self, type_str: impl Iterator<Item = &'a str>) -> Option<Arc<V>>
|
|
||||||
where V::Msg: Clone {
|
|
||||||
self.get_data_port::<V>(type_str)?.get_view()
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO
|
|
||||||
pub fn get_seq_view<'a, T: Clone>(&self, type_str: impl Iterator<Item = &'a str>) -> Option<OuterViewPort<dyn SingletonView<Item = T>>> {
|
|
||||||
self.get_data_view::<dyn SequenceView<Item = NestedNode>>(type_str)
|
|
||||||
.unwrap()
|
|
||||||
.map({
|
|
||||||
move |node| {
|
|
||||||
node.get_data_view::<dyn SingletonView<Item = T>>().get()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
pub fn get_edit<T: Send + Sync + 'static>(&self) -> Option<Arc<RwLock<T>>> {
|
|
||||||
if let Some(edit) = self.editor.get() {
|
|
||||||
if let Ok(edit) = edit.downcast::<RwLock<T>>() {
|
|
||||||
Some(edit)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
impl TreeType for NestedNode {
|
|
||||||
fn get_type(&self, addr: &TreeAddr) -> TypeLadder {
|
|
||||||
if let Some(editor) = self.editor {
|
|
||||||
editor.read().unwrap().get_type(addr)
|
|
||||||
} else {
|
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* TODO: remove that at some point
|
|
||||||
*/
|
|
||||||
impl TerminalEditor for NestedNode {
|
|
||||||
fn get_term_view(&self) -> OuterViewPort<dyn TerminalView> {
|
|
||||||
self.get_view()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_terminal_event(&mut self, event: &TerminalEvent) -> TerminalEditorResult {
|
|
||||||
let buf = SingletonBuffer::new(event.clone());
|
|
||||||
|
|
||||||
if let Some(cmd) = self.cmd.get() {
|
|
||||||
cmd.write().unwrap().send_cmd_obj(
|
|
||||||
ReprTree::new_leaf(
|
|
||||||
self.ctx.read().unwrap().type_term_from_str("TerminalEvent").unwrap(),
|
|
||||||
AnyOuterViewPort::from(buf.get_port())
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
TerminalEditorResult::Continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TreeNav for NestedNode {
|
|
||||||
fn get_cursor(&self) -> TreeCursor {
|
|
||||||
if let Some(tn) = self.tree_nav.get() {
|
|
||||||
tn.read().unwrap().get_cursor()
|
|
||||||
} else {
|
|
||||||
TreeCursor::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_addr_view(&self) -> OuterViewPort<dyn SequenceView<Item = isize>> {
|
|
||||||
if let Some(tn) = self.tree_nav.get() {
|
|
||||||
tn.read().unwrap().get_addr_view()
|
|
||||||
} else {
|
|
||||||
OuterViewPort::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_mode_view(&self) -> OuterViewPort<dyn SingletonView<Item = ListCursorMode>> {
|
|
||||||
if let Some(tn) = self.tree_nav.get() {
|
|
||||||
tn.read().unwrap().get_mode_view()
|
|
||||||
} else {
|
|
||||||
OuterViewPort::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_cursor_warp(&self) -> TreeCursor {
|
|
||||||
if let Some(tn) = self.tree_nav.get() {
|
|
||||||
tn.read().unwrap().get_cursor_warp()
|
|
||||||
} else {
|
|
||||||
TreeCursor::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_height(&self, op: &TreeHeightOp) -> usize {
|
|
||||||
if let Some(tn) = self.tree_nav.get() {
|
|
||||||
tn.read().unwrap().get_height( op )
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn goby(&mut self, direction: Vector2<isize>) -> TreeNavResult {
|
|
||||||
if let Some(tn) = self.tree_nav.get() {
|
|
||||||
tn.write().unwrap().goby(direction)
|
|
||||||
} else {
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn goto(&mut self, new_cursor: TreeCursor) -> TreeNavResult {
|
|
||||||
if let Some(tn) = self.tree_nav.get() {
|
|
||||||
tn.write().unwrap().goto(new_cursor)
|
|
||||||
} else {
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ObjCommander for NestedNode {
|
|
||||||
fn send_cmd_obj(&mut self, cmd_obj: Arc<RwLock<ReprTree>>) -> TreeNavResult {
|
|
||||||
if let Some(cmd) = self.cmd.get() {
|
|
||||||
// todo: filter out tree-nav cmds and send them to tree_nav
|
|
||||||
cmd.write().unwrap().send_cmd_obj(cmd_obj)
|
|
||||||
} else {
|
|
||||||
TreeNavResult::Exit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Diagnostics for NestedNode {
|
|
||||||
fn get_msg_port(&self) -> OuterViewPort<dyn SequenceView<Item = Message>> {
|
|
||||||
self.get_diag()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
|
|
||||||
use {
|
|
||||||
laddertypes::{TypeTerm, TypeID},
|
|
||||||
crate::{
|
|
||||||
tree::{TreeAddr}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub trait TreeType {
|
|
||||||
fn get_type(&self, addr: &TreeAddr) -> Vec<TypeTerm> {
|
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
71
nested/src/tree_nav.rs
Normal file
71
nested/src/tree_nav.rs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
|
||||||
|
use crate::list::ListCursorMode;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||||
|
pub enum TreeNavResult {
|
||||||
|
Continue,
|
||||||
|
Exit
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
impl From<TreeNavResult> for TerminalEditorResult {
|
||||||
|
fn from(v: TreeNavResult) -> TerminalEditorResult {
|
||||||
|
match v {
|
||||||
|
TreeNavResult::Continue => TerminalEditorResult::Continue,
|
||||||
|
TreeNavResult::Exit => TerminalEditorResult::Exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
#[derive(Clone, Eq, PartialEq)]
|
||||||
|
pub struct TreeCursor {
|
||||||
|
pub leaf_mode: ListCursorMode,
|
||||||
|
pub tree_addr: Vec<usize>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TreeCursor {
|
||||||
|
fn default() -> Self {
|
||||||
|
TreeCursor {
|
||||||
|
leaf_mode: ListCursorMode::Select,
|
||||||
|
tree_addr: vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait TreeNav {
|
||||||
|
fn up(&mut self) -> TreeNavResult {
|
||||||
|
TreeNavResult::Exit
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dn(&mut self) -> TreeNavResult {
|
||||||
|
TreeNavResult::Exit
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pxev(&mut self) -> TreeNavResult {
|
||||||
|
TreeNavResult::Exit
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nexd(&mut self) -> TreeNavResult {
|
||||||
|
TreeNavResult::Exit
|
||||||
|
}
|
||||||
|
|
||||||
|
fn goto_home(&mut self) -> TreeNavResult {
|
||||||
|
TreeNavResult::Exit
|
||||||
|
}
|
||||||
|
|
||||||
|
fn goto_end(&mut self) -> TreeNavResult {
|
||||||
|
TreeNavResult::Exit
|
||||||
|
}
|
||||||
|
|
||||||
|
fn goto(&mut self, new_cursor: TreeCursor) -> TreeNavResult {
|
||||||
|
TreeNavResult::Exit
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_cursor(&self) -> TreeCursor {
|
||||||
|
TreeCursor::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use crate::terminal::{TerminalView, TerminalEditor};
|
||||||
|
|
||||||
|
pub trait TerminalTreeEditor = TerminalEditor + TreeNav;
|
||||||
|
|
|
@ -1,433 +0,0 @@
|
||||||
use {
|
|
||||||
r3vi::{view::{OuterViewPort, singleton::*}, buffer::{singleton::*}},
|
|
||||||
laddertypes::{TypeDict, TypeTerm, TypeID},
|
|
||||||
crate::{
|
|
||||||
type_system::{ReprTree},
|
|
||||||
tree::NestedNode
|
|
||||||
},
|
|
||||||
std::{
|
|
||||||
collections::HashMap,
|
|
||||||
sync::{Arc, RwLock},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
|
||||||
pub enum MorphismMode {
|
|
||||||
/// Isomorphism
|
|
||||||
/// e.g. `( PositionalInteger 10 BigEndian ) <~> ( PositionalInteger 16 LittleEndian )`
|
|
||||||
Iso,
|
|
||||||
|
|
||||||
/// Monomorphism, i.e. injective functions,
|
|
||||||
/// upcast-view, downcast-control, semantic gain
|
|
||||||
/// e.g. `( Sequence ( Digit 16 ) ) ~> ( PositionalInteger 16 LittleEndian )`
|
|
||||||
Mono,
|
|
||||||
|
|
||||||
/// Epimorphsim, i.e. surjective functions,
|
|
||||||
/// upcast-control, downcast-view, possible loss of entropy
|
|
||||||
/// e.g. `( Ascii ) ~> ( Digit 16 )`
|
|
||||||
Epi,
|
|
||||||
|
|
||||||
/// Any other function
|
|
||||||
Any,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Hash, PartialEq, Eq, Debug)]
|
|
||||||
pub struct MorphismType {
|
|
||||||
// pub mode: MorphismMode,
|
|
||||||
pub src_type: Option<TypeTerm>,
|
|
||||||
pub dst_type: TypeTerm,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Hash, Eq, PartialEq)]
|
|
||||||
pub struct MorphismTypePattern {
|
|
||||||
pub src_tyid: Option<TypeID>,
|
|
||||||
pub dst_tyid: TypeID
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MorphismType {
|
|
||||||
pub fn to_str(&self, ctx: &Context) -> String {
|
|
||||||
format!("{:?} -> {:?}",
|
|
||||||
if let Some(t) = self.src_type.as_ref() {
|
|
||||||
ctx.type_term_to_str(t)
|
|
||||||
} else {
|
|
||||||
"None".into()
|
|
||||||
},
|
|
||||||
ctx.type_term_to_str(&self.dst_type))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MorphismTypePattern {
|
|
||||||
pub fn to_str(&self, ctx: &Context) -> String {
|
|
||||||
format!("{:?} -> {:?}",
|
|
||||||
if let Some(t) = self.src_tyid.as_ref() {
|
|
||||||
ctx.type_term_to_str(&TypeTerm::TypeID(t.clone()))
|
|
||||||
} else {
|
|
||||||
"None".into()
|
|
||||||
},
|
|
||||||
ctx.type_term_to_str(&TypeTerm::TypeID(self.dst_tyid.clone())))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<MorphismType> for MorphismTypePattern {
|
|
||||||
fn from(value: MorphismType) -> MorphismTypePattern {
|
|
||||||
fn strip( x: &TypeTerm ) -> TypeID {
|
|
||||||
match x {
|
|
||||||
TypeTerm::TypeID(id) => id.clone(),
|
|
||||||
TypeTerm::App(args) => strip(&args[0]),
|
|
||||||
TypeTerm::Ladder(args) => strip(&args[0]),
|
|
||||||
_ => unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MorphismTypePattern {
|
|
||||||
src_tyid: value.src_type.map(|x| strip(&x)),
|
|
||||||
dst_tyid: strip(&value.dst_type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Context {
|
|
||||||
/// assigns a name to every type
|
|
||||||
pub type_dict: Arc<RwLock<TypeDict>>,
|
|
||||||
|
|
||||||
/// named vertices of the graph
|
|
||||||
nodes: HashMap< String, NestedNode >,
|
|
||||||
|
|
||||||
/// todo: beautify
|
|
||||||
/// types that can be edited as lists
|
|
||||||
pub list_types: Vec< TypeID >,
|
|
||||||
pub meta_chars: Vec< char >,
|
|
||||||
|
|
||||||
/// graph constructors
|
|
||||||
/// TODO: move into separate struct MorphismMap or something
|
|
||||||
morphisms: HashMap<
|
|
||||||
MorphismTypePattern,
|
|
||||||
Arc<
|
|
||||||
dyn Fn( NestedNode, TypeTerm ) -> Option<NestedNode>
|
|
||||||
+ Send + Sync
|
|
||||||
>
|
|
||||||
>,
|
|
||||||
|
|
||||||
/// recursion
|
|
||||||
parent: Option<Arc<RwLock<Context>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Context {
|
|
||||||
fn default() -> Context {
|
|
||||||
let mut ctx = Context::new();
|
|
||||||
|
|
||||||
ctx.add_list_typename("Sequence");
|
|
||||||
ctx.add_synonym("Seq", "Sequence");
|
|
||||||
ctx.add_list_typename("SepSeq");
|
|
||||||
ctx.add_typename("NestedNode");
|
|
||||||
ctx.add_typename("TerminalEvent");
|
|
||||||
|
|
||||||
crate::editors::list::init_ctx( &mut ctx );
|
|
||||||
crate::editors::char::init_ctx( &mut ctx );
|
|
||||||
crate::editors::integer::init_ctx( &mut ctx );
|
|
||||||
crate::editors::typeterm::init_ctx( &mut ctx );
|
|
||||||
|
|
||||||
ctx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Context {
|
|
||||||
pub fn with_parent(parent: Option<Arc<RwLock<Context>>>) -> Self {
|
|
||||||
Context {
|
|
||||||
type_dict: match parent.as_ref() {
|
|
||||||
Some(p) => p.read().unwrap().type_dict.clone(),
|
|
||||||
None => Arc::new(RwLock::new(TypeDict::new()))
|
|
||||||
},
|
|
||||||
morphisms: HashMap::new(),
|
|
||||||
nodes: HashMap::new(),
|
|
||||||
list_types: match parent.as_ref() {
|
|
||||||
Some(p) => p.read().unwrap().list_types.clone(),
|
|
||||||
None => Vec::new()
|
|
||||||
},
|
|
||||||
meta_chars: match parent.as_ref() {
|
|
||||||
Some(p) => p.read().unwrap().meta_chars.clone(),
|
|
||||||
None => Vec::new()
|
|
||||||
},
|
|
||||||
parent,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Context::with_parent(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn depth(&self) -> usize {
|
|
||||||
if let Some(parent) = self.parent.as_ref() {
|
|
||||||
parent.read().unwrap().depth() + 1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse(ctx: &Arc<RwLock<Self>>, s: &str) -> TypeTerm {
|
|
||||||
ctx.read().unwrap().type_term_from_str(s).expect("could not parse type term")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_typename(&mut self, tn: &str) -> TypeID {
|
|
||||||
self.type_dict.write().unwrap().add_typename(tn.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_varname(&mut self, vn: &str) -> TypeID {
|
|
||||||
self.type_dict.write().unwrap().add_varname(vn.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_synonym(&mut self, new: &str, old: &str) {
|
|
||||||
self.type_dict.write().unwrap().add_synonym(new.to_string(), old.to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_list_typename(&mut self, tn: &str) {
|
|
||||||
let tid = self.add_typename(tn);
|
|
||||||
self.list_types.push( tid );
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_list_type(&self, t: &TypeTerm) -> bool {
|
|
||||||
match t {
|
|
||||||
TypeTerm::TypeID(id) => {
|
|
||||||
self.list_types.contains(id)
|
|
||||||
}
|
|
||||||
TypeTerm::Ladder(args) |
|
|
||||||
TypeTerm::App(args) => {
|
|
||||||
if args.len() > 0 {
|
|
||||||
if self.is_list_type(&args[0]) {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_typeid(&self, tn: &str) -> Option<TypeID> {
|
|
||||||
self.type_dict.read().unwrap().get_typeid(&tn.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_fun_typeid(&self, tn: &str) -> Option<u64> {
|
|
||||||
match self.get_typeid(tn) {
|
|
||||||
Some(TypeID::Fun(x)) => Some(x),
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_typename(&self, tid: &TypeID) -> Option<String> {
|
|
||||||
self.type_dict.read().unwrap().get_typename(tid)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_var_typeid(&self, tn: &str) -> Option<u64> {
|
|
||||||
match self.get_typeid(tn) {
|
|
||||||
Some(TypeID::Var(x)) => Some(x),
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn type_term_from_str(&self, tn: &str) -> Result<TypeTerm, laddertypes::parser::ParseError> {
|
|
||||||
self.type_dict.write().unwrap().parse(&tn)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn type_term_to_str(&self, t: &TypeTerm) -> String {
|
|
||||||
self.type_dict.read().unwrap().unparse(&t)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_node_ctor(&mut self, tn: &str, mk_editor: Arc<dyn Fn(Arc<RwLock<Self>>, TypeTerm, OuterViewPort<dyn SingletonView<Item = usize>>) -> Option<NestedNode> + Send + Sync>) {
|
|
||||||
let dict = self.type_dict.clone();
|
|
||||||
let mut dict = dict.write().unwrap();
|
|
||||||
|
|
||||||
let tyid =
|
|
||||||
if let Some(tyid) = dict.get_typeid(&tn.into()) {
|
|
||||||
tyid
|
|
||||||
} else {
|
|
||||||
dict.add_typename(tn.into())
|
|
||||||
};
|
|
||||||
|
|
||||||
let morphism_pattern = MorphismTypePattern {
|
|
||||||
src_tyid: None,
|
|
||||||
dst_tyid: tyid
|
|
||||||
};
|
|
||||||
|
|
||||||
drop(dict);
|
|
||||||
|
|
||||||
self.add_morphism(morphism_pattern, Arc::new(move |node, dst_type| {
|
|
||||||
mk_editor(node.ctx.clone(), dst_type, node.depth)
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_morphism(
|
|
||||||
&mut self,
|
|
||||||
morph_type_pattern: MorphismTypePattern,
|
|
||||||
morph_fn: Arc<
|
|
||||||
dyn Fn( NestedNode, TypeTerm ) -> Option<NestedNode>
|
|
||||||
+ Send + Sync
|
|
||||||
>
|
|
||||||
) {
|
|
||||||
self.morphisms.insert(morph_type_pattern, morph_fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_morphism(&self, ty: MorphismType) -> Option<Arc<dyn Fn(NestedNode, TypeTerm) -> Option<NestedNode> + Send + Sync>> {
|
|
||||||
let pattern = MorphismTypePattern::from(ty.clone());
|
|
||||||
|
|
||||||
if let Some(morphism) = self.morphisms.get( &pattern ) {
|
|
||||||
Some(morphism.clone())
|
|
||||||
} else {
|
|
||||||
self.parent.as_ref()?
|
|
||||||
.read().unwrap()
|
|
||||||
.get_morphism(ty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn make_node(ctx: &Arc<RwLock<Self>>, type_term: TypeTerm, depth: OuterViewPort<dyn SingletonView<Item = usize>>) -> Option<NestedNode> {
|
|
||||||
let mk_node = ctx.read().unwrap().get_morphism(MorphismType {
|
|
||||||
src_type: None,
|
|
||||||
dst_type: type_term.clone()
|
|
||||||
}).expect(&format!("morphism {}", ctx.read().unwrap().type_term_to_str(&type_term)));
|
|
||||||
|
|
||||||
/* create new context per node ?? too heavy.. whats the reason? TODO */
|
|
||||||
|
|
||||||
let new_ctx = Arc::new(RwLock::new(Context::with_parent(Some(ctx.clone()))));
|
|
||||||
|
|
||||||
mk_node(
|
|
||||||
NestedNode::new(new_ctx, ReprTree::new_arc(type_term.clone()), depth),
|
|
||||||
type_term
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn morph_node(mut node: NestedNode, dst_type: TypeTerm) -> NestedNode {
|
|
||||||
let src_type = node.data.read().unwrap().get_type().clone();
|
|
||||||
let pattern = MorphismType { src_type: Some(src_type), dst_type: dst_type.clone() };
|
|
||||||
|
|
||||||
/* it is not univesally true to always use ascend.
|
|
||||||
*/
|
|
||||||
node.data =
|
|
||||||
ReprTree::ascend(
|
|
||||||
&node.data,
|
|
||||||
dst_type.clone()
|
|
||||||
);
|
|
||||||
|
|
||||||
let m = node.ctx.read().unwrap().get_morphism(pattern.clone());
|
|
||||||
if let Some(transform) = m {
|
|
||||||
if let Some(new_node) = transform(node.clone(), dst_type) {
|
|
||||||
new_node
|
|
||||||
} else {
|
|
||||||
node.clone()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
eprintln!("could not find morphism {}", pattern.to_str(&node.ctx.read().unwrap()));
|
|
||||||
node
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// adds an object without any representations
|
|
||||||
pub fn add_obj(ctx: Arc<RwLock<Context>>, name: String, typename: &str) {
|
|
||||||
let type_tag = ctx.read().unwrap()
|
|
||||||
.type_dict.write().unwrap()
|
|
||||||
.parse(typename).unwrap();
|
|
||||||
|
|
||||||
if let Some(node) = Context::make_node(&ctx, type_tag, SingletonBuffer::new(0).get_port()) {
|
|
||||||
ctx.write().unwrap().nodes.insert(name, node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_obj(&self, name: &String) -> Option<NestedNode> {
|
|
||||||
if let Some(obj) = self.nodes.get(name) {
|
|
||||||
Some(obj.clone())
|
|
||||||
} else if let Some(parent) = self.parent.as_ref() {
|
|
||||||
parent.read().unwrap().get_obj(name)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
pub fn get_obj_port<'a, V: View + ?Sized + 'static>(
|
|
||||||
&self,
|
|
||||||
name: &str,
|
|
||||||
type_ladder: impl Iterator<Item = &'a str>,
|
|
||||||
) -> Option<OuterViewPort<V>>
|
|
||||||
where
|
|
||||||
V::Msg: Clone,
|
|
||||||
{
|
|
||||||
self.get_obj(&name.into())?
|
|
||||||
.downcast_ladder(type_ladder.map(|tn| self.type_dict.type_term_from_str(tn).unwrap()))?
|
|
||||||
.get_port()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_repr<'a>(
|
|
||||||
&mut self,
|
|
||||||
name: &str,
|
|
||||||
type_ladder: impl Iterator<Item = &'a str>,
|
|
||||||
port: AnyOuterViewPort,
|
|
||||||
) {
|
|
||||||
self.get_obj(&name.to_string())
|
|
||||||
.unwrap()
|
|
||||||
.repr
|
|
||||||
.write()
|
|
||||||
.unwrap()
|
|
||||||
.insert_leaf(
|
|
||||||
type_ladder.map(|tn| self.type_dict.type_term_from_str(tn).unwrap()),
|
|
||||||
port,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn epi_cast(&mut self, name: &str, typename: &str) {
|
|
||||||
let dst_type = self.type_dict.type_term_from_str(typename).unwrap();
|
|
||||||
let old_obj = self.objects.get(&name.to_string()).unwrap().clone();
|
|
||||||
let new_obj = if let Some(ctor) = self.morphism_constructors.get(&MorphismType {
|
|
||||||
mode: MorphismMode::Epi,
|
|
||||||
src_type: old_obj.type_tag.clone(),
|
|
||||||
dst_type: dst_type.clone(),
|
|
||||||
}) {
|
|
||||||
ctor(old_obj.clone())
|
|
||||||
} else {
|
|
||||||
Arc::new(RwLock::new(ReprTree::new(dst_type)))
|
|
||||||
};
|
|
||||||
|
|
||||||
new_obj
|
|
||||||
.repr
|
|
||||||
.write()
|
|
||||||
.unwrap()
|
|
||||||
.insert_branch(old_obj.type_tag, old_obj.repr);
|
|
||||||
|
|
||||||
self.objects.insert(name.to_string(), new_obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mono_view<'a, V: View + ?Sized + 'static>(
|
|
||||||
&mut self,
|
|
||||||
name: &str,
|
|
||||||
type_ladder: impl Iterator<Item = &'a str>,
|
|
||||||
) -> Option<OuterViewPort<V>>
|
|
||||||
where
|
|
||||||
V::Msg: Clone,
|
|
||||||
{
|
|
||||||
if let Some(p) = self.get_obj_port(name, type_ladder) {
|
|
||||||
Some(p)
|
|
||||||
} else {
|
|
||||||
// todo : add repr with morphism constructor (if one exists)
|
|
||||||
/*
|
|
||||||
if let Some(ctor) = self.morphism_constructors.get(
|
|
||||||
&MorphismType {
|
|
||||||
mode: MorphismMode::Mono,
|
|
||||||
src_type: old_obj.type_tag.clone(),
|
|
||||||
dst_type:
|
|
||||||
}
|
|
||||||
)
|
|
||||||
*/
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
pub mod context;
|
|
||||||
pub mod repr_tree;
|
|
||||||
|
|
||||||
pub use {
|
|
||||||
context::{Context, MorphismMode, MorphismType, MorphismTypePattern},
|
|
||||||
repr_tree::ReprTree
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,235 +0,0 @@
|
||||||
use {
|
|
||||||
r3vi::view::{AnyOuterViewPort, OuterViewPort, View},
|
|
||||||
laddertypes::{TypeTerm},
|
|
||||||
crate::{
|
|
||||||
type_system::{Context}
|
|
||||||
},
|
|
||||||
std::{
|
|
||||||
collections::HashMap,
|
|
||||||
sync::{Arc, RwLock},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ReprTree {
|
|
||||||
type_tag: TypeTerm,
|
|
||||||
port: Option<AnyOuterViewPort>,
|
|
||||||
branches: HashMap<TypeTerm, Arc<RwLock<ReprTree>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Debug for ReprTree {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(f, "type: {:?}", self.type_tag)?;
|
|
||||||
|
|
||||||
for (_k,x) in self.branches.iter() {
|
|
||||||
write!(f, "child: {:?}", x)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ReprTree {
|
|
||||||
pub fn new(type_tag: impl Into<TypeTerm>) -> Self {
|
|
||||||
ReprTree {
|
|
||||||
type_tag: type_tag.into(),
|
|
||||||
port: None,
|
|
||||||
branches: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_arc(type_tag: impl Into<TypeTerm>) -> Arc<RwLock<Self>> {
|
|
||||||
Arc::new(RwLock::new(Self::new(type_tag)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_type(&self) -> &TypeTerm {
|
|
||||||
&self.type_tag
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_char(ctx: &Arc<RwLock<Context>>, c: char) -> Arc<RwLock<Self>> {
|
|
||||||
let buf = r3vi::buffer::singleton::SingletonBuffer::<char>::new(c);
|
|
||||||
ReprTree::new_leaf(
|
|
||||||
Context::parse(ctx, "Char"),
|
|
||||||
buf.get_port().into()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_leaf(type_tag: impl Into<TypeTerm>, port: AnyOuterViewPort) -> Arc<RwLock<Self>> {
|
|
||||||
let mut tree = ReprTree::new(type_tag.into());
|
|
||||||
tree.insert_leaf(vec![].into_iter(), port);
|
|
||||||
Arc::new(RwLock::new(tree))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_branch(&mut self, repr: Arc<RwLock<ReprTree>>) {
|
|
||||||
self.branches.insert(repr.clone().read().unwrap().type_tag.clone(), repr.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert_leaf(
|
|
||||||
&mut self,
|
|
||||||
mut type_ladder: impl Iterator<Item = TypeTerm>,
|
|
||||||
port: AnyOuterViewPort,
|
|
||||||
) {
|
|
||||||
if let Some(type_term) = type_ladder.next() {
|
|
||||||
if let Some(next_repr) = self.branches.get(&type_term) {
|
|
||||||
next_repr.write().unwrap().insert_leaf(type_ladder, port);
|
|
||||||
} else {
|
|
||||||
let mut next_repr = ReprTree::new(type_term.clone());
|
|
||||||
next_repr.insert_leaf(type_ladder, port);
|
|
||||||
self.insert_branch(Arc::new(RwLock::new(next_repr)));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.port = Some(port);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
|
||||||
|
|
||||||
pub fn get_port<V: View + ?Sized + 'static>(&self) -> Option<OuterViewPort<V>>
|
|
||||||
where
|
|
||||||
V::Msg: Clone,
|
|
||||||
{
|
|
||||||
Some(
|
|
||||||
self.port
|
|
||||||
.clone()?
|
|
||||||
.downcast::<V>()
|
|
||||||
.ok()?
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_view<V: View + ?Sized + 'static>(&self) -> Option<Arc<V>>
|
|
||||||
where
|
|
||||||
V::Msg: Clone,
|
|
||||||
{
|
|
||||||
self.get_port::<V>()?
|
|
||||||
.get_view()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn descend(&self, dst_type: impl Into<TypeTerm>) -> Option<Arc<RwLock<ReprTree>>> {
|
|
||||||
self.branches.get(&dst_type.into()).cloned()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn descend_ladder(rt: &Arc<RwLock<Self>>, mut repr_ladder: impl Iterator<Item = TypeTerm>) -> Option<Arc<RwLock<ReprTree>>> {
|
|
||||||
if let Some(first) = repr_ladder.next() {
|
|
||||||
let rt = rt.read().unwrap();
|
|
||||||
repr_ladder.fold(
|
|
||||||
rt.descend(first),
|
|
||||||
|s, t| s?.read().unwrap().descend(t))
|
|
||||||
} else {
|
|
||||||
Some(rt.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ascend(rt: &Arc<RwLock<Self>>, type_term: impl Into<TypeTerm>) -> Arc<RwLock<ReprTree>> {
|
|
||||||
let mut n = Self::new(type_term);
|
|
||||||
n.insert_branch(rt.clone());
|
|
||||||
Arc::new(RwLock::new(n))
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
pub fn add_iso_repr(
|
|
||||||
&self,
|
|
||||||
type_ladder: impl Iterator<Item = TypeTerm>,
|
|
||||||
morphism_constructors: &HashMap<MorphismType, Box<dyn Fn(Object) -> Object>>,
|
|
||||||
) {
|
|
||||||
let mut cur_repr = self.repr.clone();
|
|
||||||
|
|
||||||
for dst_type in type_ladder {
|
|
||||||
if let Some(next_repr) = self.repr.read().unwrap().branches.get(&dst_type) {
|
|
||||||
// go deeper
|
|
||||||
cur_repr = next_repr.clone();
|
|
||||||
} else {
|
|
||||||
// search for morphism constructor and insert new repr
|
|
||||||
let mut obj = None;
|
|
||||||
|
|
||||||
for src_type in cur_repr.read().unwrap().branches.keys() {
|
|
||||||
if let Some(ctor) = morphism_constructors.get(&MorphismType {
|
|
||||||
mode: MorphismMode::Iso,
|
|
||||||
src_type: src_type.clone(),
|
|
||||||
dst_type: dst_type.clone(),
|
|
||||||
}) {
|
|
||||||
let new_obj = ctor(Object {
|
|
||||||
type_tag: src_type.clone(),
|
|
||||||
repr: cur_repr
|
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.branches
|
|
||||||
.get(&src_type)
|
|
||||||
.unwrap()
|
|
||||||
.clone(),
|
|
||||||
});
|
|
||||||
|
|
||||||
assert!(new_obj.type_tag == dst_type);
|
|
||||||
|
|
||||||
obj = Some(new_obj);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(obj) = obj {
|
|
||||||
cur_repr
|
|
||||||
.write()
|
|
||||||
.unwrap()
|
|
||||||
.insert_branch(obj.type_tag, obj.repr);
|
|
||||||
} else {
|
|
||||||
panic!("could not find matching isomorphism!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_mono_repr<'a>(
|
|
||||||
&self,
|
|
||||||
type_ladder: impl Iterator<Item = TypeTerm>,
|
|
||||||
morphism_constructors: &HashMap<MorphismType, Box<dyn Fn(Object) -> Object>>,
|
|
||||||
) {
|
|
||||||
let mut cur_type = self.type_tag.clone();
|
|
||||||
let mut cur_repr = self.repr.clone();
|
|
||||||
|
|
||||||
for dst_type in type_ladder {
|
|
||||||
if let Some(next_repr) = self.repr.read().unwrap().branches.get(&dst_type) {
|
|
||||||
// go deeper
|
|
||||||
cur_type = dst_type;
|
|
||||||
cur_repr = next_repr.clone();
|
|
||||||
} else {
|
|
||||||
if let Some(constructor) = morphism_constructors.get(&MorphismType {
|
|
||||||
mode: MorphismMode::Mono,
|
|
||||||
src_type: cur_type.clone(),
|
|
||||||
dst_type: dst_type.clone(),
|
|
||||||
}) {
|
|
||||||
let new_obj = constructor(Object {
|
|
||||||
type_tag: cur_type.clone(),
|
|
||||||
repr: cur_repr
|
|
||||||
.read()
|
|
||||||
.unwrap()
|
|
||||||
.branches
|
|
||||||
.get(&cur_type)
|
|
||||||
.unwrap()
|
|
||||||
.clone(),
|
|
||||||
});
|
|
||||||
|
|
||||||
assert!(new_obj.type_tag == dst_type);
|
|
||||||
cur_repr
|
|
||||||
.write()
|
|
||||||
.unwrap()
|
|
||||||
.insert_branch(new_obj.type_tag.clone(), new_obj.repr.clone());
|
|
||||||
|
|
||||||
cur_type = new_obj.type_tag;
|
|
||||||
cur_repr = new_obj.repr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace with higher-level type in which self is a repr branch
|
|
||||||
pub fn epi_cast<'a>(
|
|
||||||
&self,
|
|
||||||
_type_ladder: impl Iterator<Item = TypeTerm>,
|
|
||||||
_morphism_constructors: &HashMap<MorphismType, Box<dyn Fn(Object) -> Object>>,
|
|
||||||
) {
|
|
||||||
// todo
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
use {
|
|
||||||
crate::terminal::TerminalStyle,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn bg_style_from_depth(depth: usize) -> TerminalStyle {
|
|
||||||
match depth {
|
|
||||||
0 => TerminalStyle::bg_color((150,80,230)),
|
|
||||||
1 => TerminalStyle::bg_color((66,66,66)),
|
|
||||||
2 => TerminalStyle::bg_color((44,44,44)),
|
|
||||||
3 => TerminalStyle::bg_color((33,33,33)),
|
|
||||||
4 => TerminalStyle::bg_color((28,28,28)),
|
|
||||||
5 => TerminalStyle::bg_color((21,21,21)),
|
|
||||||
_ => TerminalStyle::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fg_style_from_depth(depth: usize) -> TerminalStyle {
|
|
||||||
if depth == 0 {
|
|
||||||
TerminalStyle::fg_color((200, 200, 200))
|
|
||||||
} else {
|
|
||||||
match depth % 5 {
|
|
||||||
0 => TerminalStyle::fg_color((128, 106, 97)),
|
|
||||||
1 => TerminalStyle::fg_color((100, 120, 232)),
|
|
||||||
2 => TerminalStyle::fg_color((180, 100, 96)),
|
|
||||||
3 => TerminalStyle::fg_color((188, 155, 18)),
|
|
||||||
4 => TerminalStyle::fg_color((135, 182, 134)),
|
|
||||||
_ => TerminalStyle::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
pub mod bimap;
|
|
||||||
pub mod modulo;
|
|
||||||
pub mod color;
|
|
||||||
|
|
||||||
pub use modulo::modulo;
|
|
||||||
pub use bimap::Bimap;
|
|
|
@ -1,10 +0,0 @@
|
||||||
|
|
||||||
pub fn modulo(a: isize, b: isize) -> isize {
|
|
||||||
if b > 0 {
|
|
||||||
((a % b) + b) % b
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
134
nested/src/vec/buffer.rs
Normal file
134
nested/src/vec/buffer.rs
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
use {
|
||||||
|
std::{
|
||||||
|
sync::Arc,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
},
|
||||||
|
std::sync::RwLock,
|
||||||
|
crate::{
|
||||||
|
core::{View, Observer, ObserverBroadcast, InnerViewPort},
|
||||||
|
vec::VecDiff
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<T> View for Vec<T>
|
||||||
|
where T: Clone + Send + Sync + 'static {
|
||||||
|
type Msg = VecDiff<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct VecBuffer<T>
|
||||||
|
where T: Clone + Send + Sync + 'static
|
||||||
|
{
|
||||||
|
data: Arc<RwLock<Vec<T>>>,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<RwLock<Vec<T>>>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> VecBuffer<T>
|
||||||
|
where T: Clone + Send + Sync + 'static
|
||||||
|
{
|
||||||
|
pub fn with_data(
|
||||||
|
data: Vec<T>,
|
||||||
|
port: InnerViewPort<RwLock<Vec<T>>>
|
||||||
|
) -> Self {
|
||||||
|
let mut b = VecBuffer::new(port);
|
||||||
|
for x in data.into_iter() {
|
||||||
|
b.push(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
b
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(port: InnerViewPort<RwLock<Vec<T>>>) -> Self {
|
||||||
|
let data = Arc::new(RwLock::new(Vec::new()));
|
||||||
|
port.set_view(Some(data.clone()));
|
||||||
|
VecBuffer { data, cast: port.get_broadcast() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply_diff(&mut self, diff: VecDiff<T>) {
|
||||||
|
let mut data = self.data.write().unwrap();
|
||||||
|
match &diff {
|
||||||
|
VecDiff::Clear => { data.clear(); },
|
||||||
|
VecDiff::Push(val) => { data.push(val.clone()); },
|
||||||
|
VecDiff::Remove(idx) => { data.remove(*idx); },
|
||||||
|
VecDiff::Insert{ idx, val } => { data.insert(*idx, val.clone()); },
|
||||||
|
VecDiff::Update{ idx, val } => { data[*idx] = val.clone(); }
|
||||||
|
}
|
||||||
|
drop(data);
|
||||||
|
|
||||||
|
self.cast.notify(&diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.data.read().unwrap().len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, idx: usize) -> T {
|
||||||
|
self.data.read().unwrap()[idx].clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.apply_diff(VecDiff::Clear);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, val: T) {
|
||||||
|
self.apply_diff(VecDiff::Push(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&mut self, idx: usize) {
|
||||||
|
self.apply_diff(VecDiff::Remove(idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert(&mut self, idx: usize, val: T) {
|
||||||
|
self.apply_diff(VecDiff::Insert{ idx, val });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, idx: usize, val: T) {
|
||||||
|
self.apply_diff(VecDiff::Update{ idx, val });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mut(&mut self, idx: usize) -> MutableVecAccess<T> {
|
||||||
|
MutableVecAccess {
|
||||||
|
buf: self.clone(),
|
||||||
|
idx,
|
||||||
|
val: self.get(idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub struct MutableVecAccess<T>
|
||||||
|
where T: Clone + Send + Sync + 'static {
|
||||||
|
buf: VecBuffer<T>,
|
||||||
|
idx: usize,
|
||||||
|
val: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for MutableVecAccess<T>
|
||||||
|
where T: Clone + Send + Sync + 'static {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &T {
|
||||||
|
&self.val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> DerefMut for MutableVecAccess<T>
|
||||||
|
where T: Clone + Send + Sync + 'static {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for MutableVecAccess<T>
|
||||||
|
where T: Clone + Send + Sync + 'static {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.buf.update(self.idx, self.val.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
25
nested/src/vec/mod.rs
Normal file
25
nested/src/vec/mod.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
|
||||||
|
pub mod buffer;
|
||||||
|
pub mod vec2seq;
|
||||||
|
pub mod vec2json;
|
||||||
|
pub mod vec2bin;
|
||||||
|
|
||||||
|
pub use {
|
||||||
|
buffer::VecBuffer
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
use {
|
||||||
|
serde::{Serialize, Deserialize}
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
|
pub enum VecDiff<T> {
|
||||||
|
Clear,
|
||||||
|
Push(T),
|
||||||
|
Remove(usize),
|
||||||
|
Insert{ idx: usize, val: T },
|
||||||
|
Update{ idx: usize, val: T }
|
||||||
|
}
|
||||||
|
|
67
nested/src/vec/vec2bin.rs
Normal file
67
nested/src/vec/vec2bin.rs
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
use {
|
||||||
|
std::{
|
||||||
|
sync::Arc,
|
||||||
|
io::Write
|
||||||
|
},
|
||||||
|
std::sync::RwLock,
|
||||||
|
serde::Serialize,
|
||||||
|
crate::{
|
||||||
|
core::{Observer, OuterViewPort},
|
||||||
|
vec::VecDiff
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
/// Serialization Observer for `Vec`
|
||||||
|
pub struct VecBinWriter<T, W>
|
||||||
|
where T: Clone + Send + Sync + 'static,
|
||||||
|
W: Write + Send + Sync {
|
||||||
|
data: Option<Arc<RwLock<Vec<T>>>>,
|
||||||
|
out: RwLock<W>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<T> OuterViewPort<RwLock<Vec<T>>>
|
||||||
|
where T: Clone + Serialize + Send + Sync + 'static {
|
||||||
|
pub fn serialize_bin<W: Write + Send + Sync + 'static>(&self, out: W) -> Arc<RwLock<VecBinWriter<T, W>>> {
|
||||||
|
let writer = Arc::new(RwLock::new(
|
||||||
|
VecBinWriter {
|
||||||
|
data: None,
|
||||||
|
out: RwLock::new(out),
|
||||||
|
}
|
||||||
|
));
|
||||||
|
self.add_observer(writer.clone());
|
||||||
|
writer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, W> Observer<RwLock<Vec<T>>> for VecBinWriter<T, W>
|
||||||
|
where T: Clone + Serialize + Send + Sync + 'static,
|
||||||
|
W: Write + Send + Sync
|
||||||
|
{
|
||||||
|
fn reset(&mut self, view: Option<Arc<RwLock<Vec<T>>>>) {
|
||||||
|
self.data = view;
|
||||||
|
let mut out = self.out.write().unwrap();
|
||||||
|
|
||||||
|
out.write(&bincode::serialized_size(&VecDiff::<T>::Clear).unwrap().to_le_bytes()).expect("");
|
||||||
|
out.write(&bincode::serialize(&VecDiff::<T>::Clear).unwrap()).expect("");
|
||||||
|
|
||||||
|
if let Some(data) = self.data.as_ref() {
|
||||||
|
for x in data.read().unwrap().iter() {
|
||||||
|
out.write(&bincode::serialized_size(&VecDiff::Push(x)).unwrap().to_le_bytes()).expect("");
|
||||||
|
out.write(&bincode::serialize(&VecDiff::Push(x)).unwrap()).expect("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out.flush().expect("");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&mut self, diff: &VecDiff<T>) {
|
||||||
|
let mut out = self.out.write().unwrap();
|
||||||
|
out.write(&bincode::serialized_size(diff).unwrap().to_le_bytes()).expect("");
|
||||||
|
out.write(&bincode::serialize(diff).unwrap()).expect("");
|
||||||
|
out.flush().expect("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
91
nested/src/vec/vec2json.rs
Normal file
91
nested/src/vec/vec2json.rs
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
use {
|
||||||
|
std::{
|
||||||
|
sync::Arc,
|
||||||
|
io::Write
|
||||||
|
},
|
||||||
|
std::sync::RwLock,
|
||||||
|
async_std::{
|
||||||
|
io::{Read, ReadExt},
|
||||||
|
stream::{StreamExt}
|
||||||
|
},
|
||||||
|
serde::{Serialize, de::DeserializeOwned},
|
||||||
|
crate::{
|
||||||
|
core::{Observer, OuterViewPort},
|
||||||
|
vec::{VecDiff, VecBuffer}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
pub struct VecJsonWriter<T, W>
|
||||||
|
where T: Clone + Send + Sync + 'static,
|
||||||
|
W: Write + Send + Sync {
|
||||||
|
data: Option<Arc<RwLock<Vec<T>>>>,
|
||||||
|
out: RwLock<W>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> OuterViewPort<RwLock<Vec<T>>>
|
||||||
|
where T: Clone + Serialize + Send + Sync + 'static {
|
||||||
|
pub fn serialize_json<W: Write + Send + Sync + 'static>(&self, out: W) -> Arc<RwLock<VecJsonWriter<T, W>>> {
|
||||||
|
let writer = Arc::new(RwLock::new(
|
||||||
|
VecJsonWriter {
|
||||||
|
data: None,
|
||||||
|
out: RwLock::new(out),
|
||||||
|
}
|
||||||
|
));
|
||||||
|
self.add_observer(writer.clone());
|
||||||
|
writer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, W> Observer<RwLock<Vec<T>>> for VecJsonWriter<T, W>
|
||||||
|
where T: Clone + Serialize + Send + Sync + 'static,
|
||||||
|
W: Write + Send + Sync
|
||||||
|
{
|
||||||
|
fn reset(&mut self, view: Option<Arc<RwLock<Vec<T>>>>) {
|
||||||
|
self.data = view;
|
||||||
|
|
||||||
|
self.out.write().unwrap().write(&serde_json::to_string(&VecDiff::<T>::Clear).unwrap().as_bytes()).expect("");
|
||||||
|
self.out.write().unwrap().write(b"\n").expect("");
|
||||||
|
|
||||||
|
if let Some(data) = self.data.as_ref() {
|
||||||
|
for x in data.read().unwrap().iter() {
|
||||||
|
self.out.write().unwrap().write(&serde_json::to_string(&VecDiff::Push(x)).unwrap().as_bytes()).expect("");
|
||||||
|
self.out.write().unwrap().write(b"\n").expect("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.out.write().unwrap().flush().expect("");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&mut self, diff: &VecDiff<T>) {
|
||||||
|
self.out.write().unwrap().write(serde_json::to_string(diff).unwrap().as_bytes()).expect("");
|
||||||
|
self.out.write().unwrap().write(b"\n").expect("");
|
||||||
|
self.out.write().unwrap().flush().expect("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> VecBuffer<T>
|
||||||
|
where T: DeserializeOwned + Clone + Send + Sync + 'static
|
||||||
|
{
|
||||||
|
pub async fn from_json<R: Read + async_std::io::Read + Unpin>(&mut self, read: R) {
|
||||||
|
let mut bytes = read.bytes();
|
||||||
|
let mut s = String::new();
|
||||||
|
while let Some(Ok(b)) = bytes.next().await {
|
||||||
|
match b {
|
||||||
|
b'\n' => {
|
||||||
|
if s.len() > 0 {
|
||||||
|
let diff = serde_json::from_str::<VecDiff<T>>(&s).expect("error parsing json");
|
||||||
|
self.apply_diff(diff);
|
||||||
|
s.clear();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
c => {
|
||||||
|
s.push(c as char);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
116
nested/src/vec/vec2seq.rs
Normal file
116
nested/src/vec/vec2seq.rs
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
use {
|
||||||
|
std::{
|
||||||
|
sync::Arc,
|
||||||
|
},
|
||||||
|
std::sync::RwLock,
|
||||||
|
crate::{
|
||||||
|
core::{View, Observer, ObserverExt, ObserverBroadcast, ViewPort, InnerViewPort, OuterViewPort},
|
||||||
|
sequence::SequenceView,
|
||||||
|
vec::VecDiff
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
/// Adapter View implementing `Sequence` for `Vec`
|
||||||
|
pub struct VecSequence<T>
|
||||||
|
where T: Clone + Send + Sync + 'static {
|
||||||
|
cur_len: usize,
|
||||||
|
data: Option<Arc<RwLock<Vec<T>>>>,
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn SequenceView<Item = T>>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<T> VecSequence<T>
|
||||||
|
where T: Clone + Send + Sync + 'static {
|
||||||
|
pub fn new(
|
||||||
|
port: InnerViewPort<dyn SequenceView<Item = T>>
|
||||||
|
) -> Arc<RwLock<Self>> {
|
||||||
|
let seq = Arc::new(RwLock::new(
|
||||||
|
VecSequence {
|
||||||
|
cur_len: 0,
|
||||||
|
data: None,
|
||||||
|
cast: port.get_broadcast()
|
||||||
|
}
|
||||||
|
));
|
||||||
|
port.set_view(Some(seq.clone()));
|
||||||
|
seq
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Observer<RwLock<Vec<T>>> for VecSequence<T>
|
||||||
|
where T: Clone + Send + Sync + 'static {
|
||||||
|
fn reset(&mut self, view: Option<Arc<RwLock<Vec<T>>>>) {
|
||||||
|
let old_len = self.cur_len;
|
||||||
|
self.data = view;
|
||||||
|
let new_len =
|
||||||
|
if let Some(data) = self.data.as_ref() {
|
||||||
|
data.read().unwrap().len()
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
self.cur_len = new_len;
|
||||||
|
self.cast.notify_each(0 .. std::cmp::max(old_len, new_len));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&mut self, diff: &VecDiff<T>) {
|
||||||
|
match diff {
|
||||||
|
VecDiff::Clear => {
|
||||||
|
self.cast.notify_each(0 .. self.cur_len);
|
||||||
|
self.cur_len = 0
|
||||||
|
},
|
||||||
|
VecDiff::Push(_) => {
|
||||||
|
self.cast.notify(&self.cur_len);
|
||||||
|
self.cur_len += 1;
|
||||||
|
},
|
||||||
|
VecDiff::Remove(idx) => {
|
||||||
|
self.cast.notify_each(*idx .. self.cur_len);
|
||||||
|
self.cur_len -= 1;
|
||||||
|
},
|
||||||
|
VecDiff::Insert{ idx, val: _ } => {
|
||||||
|
self.cur_len += 1;
|
||||||
|
self.cast.notify_each(*idx .. self.cur_len);
|
||||||
|
},
|
||||||
|
VecDiff::Update{ idx, val: _ } => {
|
||||||
|
self.cast.notify(&idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> View for VecSequence<T>
|
||||||
|
where T: Clone + Send + Sync + 'static {
|
||||||
|
type Msg = usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> SequenceView for VecSequence<T>
|
||||||
|
where T: Clone + Send + Sync + 'static {
|
||||||
|
type Item = T;
|
||||||
|
|
||||||
|
fn get(&self, idx: &usize) -> Option<T> {
|
||||||
|
self.data.as_ref()?
|
||||||
|
.read().unwrap()
|
||||||
|
.get(*idx).cloned()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> Option<usize> {
|
||||||
|
Some(self.cur_len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<<<<>>>><<>><><<>><<<*>>><<>><><<>><<<<>>>>
|
||||||
|
|
||||||
|
impl<T> OuterViewPort<RwLock<Vec<T>>>
|
||||||
|
where T: Clone + Send + Sync + 'static {
|
||||||
|
pub fn to_sequence(&self) -> OuterViewPort<dyn SequenceView<Item = T>> {
|
||||||
|
let port = ViewPort::new();
|
||||||
|
port.add_update_hook(Arc::new(self.0.clone()));
|
||||||
|
|
||||||
|
let vec_seq = VecSequence::new(port.inner());
|
||||||
|
self.add_observer(vec_seq.clone());
|
||||||
|
port.into_outer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
[toolchain]
|
# If you see this, run `rustup self update` to get rustup 1.23 or newer.
|
||||||
channel = "nightly"
|
|
||||||
|
|
||||||
|
# NOTE: above comment is for older `rustup` (before TOML support was added),
|
||||||
|
# which will treat the first line as the toolchain name, and therefore show it
|
||||||
|
# to the user in the error, instead of "error: invalid channel name '[toolchain]'".
|
||||||
|
|
||||||
|
[toolchain]
|
||||||
|
channel = "nightly-2021-10-26"
|
||||||
|
components = ["rust-src", "rustc-dev", "llvm-tools-preview"]
|
||||||
|
|
19
sdf_editor/Cargo.toml
Normal file
19
sdf_editor/Cargo.toml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
[package]
|
||||||
|
name = "sdf_editor"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
nako = {git= "https://git.exobiont.de/senvas/nako.git"}
|
||||||
|
nako_std = {git= "https://git.exobiont.de/senvas/nako.git"}
|
||||||
|
nakorender = {git="https://git.exobiont.de/senvas/nako.git", default-features = false}
|
||||||
|
nested = { path = "../nested" }
|
||||||
|
cgmath = "*"
|
||||||
|
termion = "*"
|
||||||
|
font-kit = "*"
|
||||||
|
|
||||||
|
[dependencies.async-std]
|
||||||
|
version = "1.9.0"
|
||||||
|
features = ["unstable", "attributes"]
|
288
sdf_editor/src/main.rs
Normal file
288
sdf_editor/src/main.rs
Normal file
|
@ -0,0 +1,288 @@
|
||||||
|
|
||||||
|
use{
|
||||||
|
std::{
|
||||||
|
sync::{Arc, RwLock, Mutex},
|
||||||
|
collections::HashMap
|
||||||
|
},
|
||||||
|
cgmath::{Point2, Vector2},
|
||||||
|
termion::event::{Event, Key},
|
||||||
|
nested::{
|
||||||
|
core::{
|
||||||
|
View,
|
||||||
|
ViewPort,
|
||||||
|
Observer,
|
||||||
|
ObserverExt,
|
||||||
|
OuterViewPort,
|
||||||
|
port::UpdateTask
|
||||||
|
},
|
||||||
|
singleton::{SingletonBuffer, SingletonView},
|
||||||
|
sequence::{SequenceView},
|
||||||
|
integer::{PosIntEditor},
|
||||||
|
terminal::{Terminal, TerminalAtom, TerminalStyle, TerminalView, TerminalCompositor, TerminalEvent, TerminalEditor},
|
||||||
|
list::{ListEditor},
|
||||||
|
tree_nav::{TreeNav},
|
||||||
|
sdf::SdfTerm
|
||||||
|
},
|
||||||
|
nako::{
|
||||||
|
stream::{SecondaryStream2d, PrimaryStream2d},
|
||||||
|
glam::{Vec2, Vec3, UVec2, IVec2},
|
||||||
|
operations::{
|
||||||
|
planar::primitives2d::Box2d,
|
||||||
|
volumetric::{Color, Union, Round},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
nakorender::{
|
||||||
|
backend::{Backend, LayerId, LayerId2d, LayerInfo},
|
||||||
|
marp::MarpBackend,
|
||||||
|
winit, camera::Camera2d
|
||||||
|
},
|
||||||
|
nako_std::{
|
||||||
|
text::Character
|
||||||
|
},
|
||||||
|
std::{fs::File, io::Read, mem::needs_drop, path::Path},
|
||||||
|
font_kit::font::Font,
|
||||||
|
};
|
||||||
|
|
||||||
|
// projects a Sequence of ints to a color tuple
|
||||||
|
struct ColorCollector {
|
||||||
|
src_view: Option<Arc<dyn SequenceView<Item = u32>>>,
|
||||||
|
color: SingletonBuffer<(u8, u8, u8)>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColorCollector {
|
||||||
|
fn update(&mut self) {
|
||||||
|
if let Some(l) = self.src_view.as_ref() {
|
||||||
|
let r = l.get(&0).unwrap_or(0);
|
||||||
|
let g = l.get(&1).unwrap_or(0);
|
||||||
|
let b = l.get(&2).unwrap_or(0);
|
||||||
|
|
||||||
|
self.color.set((r as u8, g as u8, b as u8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Observer<dyn SequenceView<Item = u32>> for ColorCollector {
|
||||||
|
fn reset(&mut self, new_view: Option<Arc<dyn SequenceView<Item = u32>>>) {
|
||||||
|
self.src_view = new_view;
|
||||||
|
self.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notify(&mut self, idx: &usize) {
|
||||||
|
self.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_std::main]
|
||||||
|
async fn main() {
|
||||||
|
let term_port = ViewPort::new();
|
||||||
|
let compositor = TerminalCompositor::new(term_port.inner());
|
||||||
|
|
||||||
|
let mut color_editor = ListEditor::new(
|
||||||
|
|| {
|
||||||
|
Arc::new(RwLock::new(PosIntEditor::new(16)))
|
||||||
|
},
|
||||||
|
nested::list::ListEditorStyle::HorizontalSexpr
|
||||||
|
);
|
||||||
|
|
||||||
|
color_editor.goto(nested::tree_nav::TreeCursor {
|
||||||
|
leaf_mode: nested::list::ListCursorMode::Insert,
|
||||||
|
tree_addr: vec![ 0 ]
|
||||||
|
});
|
||||||
|
|
||||||
|
let color_port = ViewPort::new();
|
||||||
|
let color_collector = Arc::new(RwLock::new(ColorCollector {
|
||||||
|
src_view: None,
|
||||||
|
color: SingletonBuffer::new((200, 200, 0), color_port.inner())
|
||||||
|
}));
|
||||||
|
|
||||||
|
let col_seq_port = color_editor.get_data_port().map(
|
||||||
|
|sub_editor| sub_editor.read().unwrap().get_value()
|
||||||
|
);
|
||||||
|
color_port.add_update_hook(Arc::new(col_seq_port.0.clone()));
|
||||||
|
col_seq_port.add_observer(
|
||||||
|
color_collector.clone()
|
||||||
|
);
|
||||||
|
|
||||||
|
compositor.write().unwrap().push(color_editor.get_term_view().offset(Vector2::new(0, 0)));
|
||||||
|
|
||||||
|
let event_loop = nakorender::winit::event_loop::EventLoop::new();
|
||||||
|
let window = nakorender::winit::window::Window::new(&event_loop).unwrap();
|
||||||
|
let mut renderer = Arc::new(Mutex::new(nakorender::marp::MarpBackend::new(&window, &event_loop)));
|
||||||
|
|
||||||
|
// terminal view
|
||||||
|
let mut sdf_term = Arc::new(RwLock::new(SdfTerm::new(renderer.clone())));
|
||||||
|
term_port.outer().add_observer(sdf_term.clone());
|
||||||
|
|
||||||
|
// color preview
|
||||||
|
let color_view = color_port.outer().get_view();
|
||||||
|
let color_layer_id = renderer.lock().unwrap().new_layer_2d();
|
||||||
|
renderer.lock().unwrap().update_camera_2d(color_layer_id, Camera2d{
|
||||||
|
extent: Vec2::new(4.0, 4.0),
|
||||||
|
location: Vec2::new(-2.0, -2.0),
|
||||||
|
rotation: 0.0
|
||||||
|
});
|
||||||
|
renderer.lock().unwrap().set_layer_info(color_layer_id.into(), LayerInfo{
|
||||||
|
extent: UVec2::new(600, 600),
|
||||||
|
location: IVec2::new(200,100)
|
||||||
|
});
|
||||||
|
|
||||||
|
event_loop.run(move |event, _target, control_flow|{
|
||||||
|
//Set to polling for now, might be overwritten
|
||||||
|
//TODO: Maybe we want to use "WAIT" for the ui thread? However, the renderer.lock().unwrap()s don't work that hard
|
||||||
|
//if nothing changes. So should be okay for a alpha style programm.
|
||||||
|
*control_flow = winit::event_loop::ControlFlow::Poll;
|
||||||
|
|
||||||
|
//now check if a rerender was requested, or if we worked on all
|
||||||
|
//events on that batch
|
||||||
|
match event{
|
||||||
|
winit::event::Event::WindowEvent{window_id: _, event: winit::event::WindowEvent::Resized(newsize)} => {
|
||||||
|
|
||||||
|
}
|
||||||
|
winit::event::Event::WindowEvent{window_id: _, event: winit::event::WindowEvent::KeyboardInput{ device_id, input, is_synthetic }} => {
|
||||||
|
if input.state == winit::event::ElementState::Pressed {
|
||||||
|
if let Some(kc) = input.virtual_keycode {
|
||||||
|
match kc {
|
||||||
|
winit::event::VirtualKeyCode::Space |
|
||||||
|
winit::event::VirtualKeyCode::Return => {
|
||||||
|
color_editor.handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::Char(' '))));
|
||||||
|
}
|
||||||
|
winit::event::VirtualKeyCode::Key0 |
|
||||||
|
winit::event::VirtualKeyCode::Numpad0 => {
|
||||||
|
color_editor.handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::Char('0'))));
|
||||||
|
}
|
||||||
|
winit::event::VirtualKeyCode::Key1 |
|
||||||
|
winit::event::VirtualKeyCode::Numpad1 => {
|
||||||
|
color_editor.handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::Char('1'))));
|
||||||
|
}
|
||||||
|
winit::event::VirtualKeyCode::Key2 |
|
||||||
|
winit::event::VirtualKeyCode::Numpad2 => {
|
||||||
|
color_editor.handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::Char('2'))));
|
||||||
|
}
|
||||||
|
winit::event::VirtualKeyCode::Key3 |
|
||||||
|
winit::event::VirtualKeyCode::Numpad3 => {
|
||||||
|
color_editor.handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::Char('3'))));
|
||||||
|
}
|
||||||
|
winit::event::VirtualKeyCode::Key4 |
|
||||||
|
winit::event::VirtualKeyCode::Numpad4 => {
|
||||||
|
color_editor.handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::Char('4'))));
|
||||||
|
}
|
||||||
|
winit::event::VirtualKeyCode::Key5 |
|
||||||
|
winit::event::VirtualKeyCode::Numpad5 => {
|
||||||
|
color_editor.handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::Char('5'))));
|
||||||
|
}
|
||||||
|
winit::event::VirtualKeyCode::Key6 |
|
||||||
|
winit::event::VirtualKeyCode::Numpad6 => {
|
||||||
|
color_editor.handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::Char('6'))));
|
||||||
|
}
|
||||||
|
winit::event::VirtualKeyCode::Key7 |
|
||||||
|
winit::event::VirtualKeyCode::Numpad7 => {
|
||||||
|
color_editor.handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::Char('7'))));
|
||||||
|
}
|
||||||
|
winit::event::VirtualKeyCode::Key8 |
|
||||||
|
winit::event::VirtualKeyCode::Numpad8 => {
|
||||||
|
color_editor.handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::Char('8'))));
|
||||||
|
}
|
||||||
|
winit::event::VirtualKeyCode::Key9 |
|
||||||
|
winit::event::VirtualKeyCode::Numpad9 => {
|
||||||
|
color_editor.handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::Char('9'))));
|
||||||
|
}
|
||||||
|
winit::event::VirtualKeyCode::A => {
|
||||||
|
color_editor.handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::Char('a'))));
|
||||||
|
}
|
||||||
|
winit::event::VirtualKeyCode::B => {
|
||||||
|
color_editor.handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::Char('b'))));
|
||||||
|
}
|
||||||
|
winit::event::VirtualKeyCode::C => {
|
||||||
|
color_editor.handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::Char('c'))));
|
||||||
|
}
|
||||||
|
winit::event::VirtualKeyCode::D => {
|
||||||
|
color_editor.handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::Char('d'))));
|
||||||
|
}
|
||||||
|
winit::event::VirtualKeyCode::E => {
|
||||||
|
color_editor.handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::Char('e'))));
|
||||||
|
}
|
||||||
|
winit::event::VirtualKeyCode::F => {
|
||||||
|
color_editor.handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::Char('f'))));
|
||||||
|
}
|
||||||
|
winit::event::VirtualKeyCode::Tab => {
|
||||||
|
color_editor.handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::Insert)));
|
||||||
|
}
|
||||||
|
winit::event::VirtualKeyCode::Delete => {
|
||||||
|
color_editor.handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::Delete)));
|
||||||
|
}
|
||||||
|
winit::event::VirtualKeyCode::Back => {
|
||||||
|
color_editor.handle_terminal_event(&TerminalEvent::Input(Event::Key(Key::Backspace)));
|
||||||
|
}
|
||||||
|
winit::event::VirtualKeyCode::Left => {
|
||||||
|
color_editor.pxev();
|
||||||
|
}
|
||||||
|
winit::event::VirtualKeyCode::Right => {
|
||||||
|
color_editor.nexd();
|
||||||
|
}
|
||||||
|
winit::event::VirtualKeyCode::Up => {
|
||||||
|
color_editor.up();
|
||||||
|
}
|
||||||
|
winit::event::VirtualKeyCode::Down => {
|
||||||
|
color_editor.dn();
|
||||||
|
color_editor.goto_home();
|
||||||
|
}
|
||||||
|
winit::event::VirtualKeyCode::Home => {
|
||||||
|
color_editor.goto_home();
|
||||||
|
}
|
||||||
|
winit::event::VirtualKeyCode::End => {
|
||||||
|
color_editor.goto_end();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
winit::event::Event::MainEventsCleared => {
|
||||||
|
window.request_redraw();
|
||||||
|
}
|
||||||
|
winit::event::Event::RedrawRequested(_) => {
|
||||||
|
color_port.update();
|
||||||
|
term_port.update();
|
||||||
|
|
||||||
|
let c = color_view.get();
|
||||||
|
let color_stream = PrimaryStream2d::new()
|
||||||
|
.push(
|
||||||
|
SecondaryStream2d::new(
|
||||||
|
Union,
|
||||||
|
Box2d {
|
||||||
|
extent: Vec2::new(0.5, 0.5)
|
||||||
|
}
|
||||||
|
).push_mod(
|
||||||
|
Color(
|
||||||
|
Vec3::new(
|
||||||
|
(c.0 as f32 / 255.0).clamp(0.0, 1.0),
|
||||||
|
(c.1 as f32 / 255.0).clamp(0.0, 1.0),
|
||||||
|
(c.2 as f32 / 255.0).clamp(0.0, 1.0),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).push_mod(
|
||||||
|
Round{radius: 0.2}
|
||||||
|
).build()
|
||||||
|
).build();
|
||||||
|
|
||||||
|
renderer.lock().unwrap().update_sdf_2d(color_layer_id, color_stream);
|
||||||
|
renderer.lock().unwrap().set_layer_order(
|
||||||
|
vec![
|
||||||
|
vec![ color_layer_id.into() ].into_iter(),
|
||||||
|
sdf_term.read().unwrap().get_order().into_iter()
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.as_slice()
|
||||||
|
);
|
||||||
|
|
||||||
|
renderer.lock().unwrap().render(&window);
|
||||||
|
}
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue