add lines exapmle
This commit is contained in:
parent
b2083aec4c
commit
e09f3e5656
4 changed files with 446 additions and 1 deletions
|
@ -11,5 +11,5 @@ members = [
|
||||||
"examples/tty-03-string",
|
"examples/tty-03-string",
|
||||||
"examples/tty-04-posint",
|
"examples/tty-04-posint",
|
||||||
"examples/tty-05-dictionary",
|
"examples/tty-05-dictionary",
|
||||||
|
"examples/tty-06-lines",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
19
examples/tty-06-lines/Cargo.toml
Normal file
19
examples/tty-06-lines/Cargo.toml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
[package]
|
||||||
|
name = "tty-06-lines"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
laddertypes = { path = "../../../lib-laddertypes" }
|
||||||
|
r3vi = { path = "../../../lib-r3vi" }
|
||||||
|
nested = { path = "../../lib-nested-core" }
|
||||||
|
nested-tty = { path = "../../lib-nested-tty" }
|
||||||
|
iterate-text = "0.0.1"
|
||||||
|
termion = "*"
|
||||||
|
cgmath = "*"
|
||||||
|
|
||||||
|
[dependencies.async-std]
|
||||||
|
version = "1.9.0"
|
||||||
|
features = ["unstable", "attributes"]
|
8
examples/tty-06-lines/README.md
Normal file
8
examples/tty-06-lines/README.md
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# tty-03-string
|
||||||
|
|
||||||
|
Similarly to `tty-02-digit`, a editor is created
|
||||||
|
but of type <List Char>.
|
||||||
|
The contents of the editor can be retrieved by
|
||||||
|
a morphism from the `EditTree` node.
|
||||||
|
To demonstrate that, the values are are mapped
|
||||||
|
to the TTY-display in different form.
|
418
examples/tty-06-lines/src/main.rs
Normal file
418
examples/tty-06-lines/src/main.rs
Normal file
|
@ -0,0 +1,418 @@
|
||||||
|
extern crate cgmath;
|
||||||
|
extern crate nested;
|
||||||
|
extern crate nested_tty;
|
||||||
|
extern crate r3vi;
|
||||||
|
extern crate termion;
|
||||||
|
|
||||||
|
use {
|
||||||
|
cgmath::{Vector2, Point2},
|
||||||
|
nested::{
|
||||||
|
editors::{ObjCommander, char::CharEditor, list::{ListEditor, ListSegment, ListSegmentSequence}},
|
||||||
|
repr_tree::{Context, ReprLeaf, ReprTree, ReprTreeExt, GenericReprTreeMorphism},
|
||||||
|
edit_tree::{EditTree, TreeNav, TreeCursor}
|
||||||
|
},
|
||||||
|
nested_tty::{
|
||||||
|
DisplaySegment, TTYApplication,
|
||||||
|
TerminalCompositor, TerminalStyle, TerminalView,
|
||||||
|
TerminalAtom, TerminalEvent,
|
||||||
|
edit_tree::cursor_widget::TreeNavExt,
|
||||||
|
TerminalProjections
|
||||||
|
},
|
||||||
|
r3vi::{
|
||||||
|
buffer::{singleton::*, vec::*},
|
||||||
|
view::{ObserverBroadcast, Observer, ObserverExt, View, ViewPort, OuterViewPort, port::UpdateTask, list::*, sequence::*, singleton::*, index::*, grid::*},
|
||||||
|
projection::projection_helper::ProjectionHelper
|
||||||
|
},
|
||||||
|
std::sync::{Arc, RwLock},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct LineDiagnostic {
|
||||||
|
range: (u32, u32),
|
||||||
|
msg: String
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LineView {
|
||||||
|
line_num: Arc< dyn SingletonView<Item = u64> >,
|
||||||
|
segments: Arc< dyn SequenceView<Item = ListSegment> >,
|
||||||
|
diagnostics: Arc< dyn SequenceView<Item = LineDiagnostic> >,
|
||||||
|
|
||||||
|
diag_buf: VecBuffer< LineDiagnostic >,
|
||||||
|
|
||||||
|
cast: Arc<RwLock<ObserverBroadcast<dyn TerminalView>>>,
|
||||||
|
out_port: OuterViewPort<dyn TerminalView>,
|
||||||
|
proj_helper: ProjectionHelper<usize, Self>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl View for LineView {
|
||||||
|
type Msg = IndexArea<Point2<i16>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndexView<Point2<i16>> for LineView {
|
||||||
|
type Item = TerminalAtom;
|
||||||
|
|
||||||
|
fn get(&self, pos: &Point2<i16>) -> Option< TerminalAtom > {
|
||||||
|
let xoff = 6;
|
||||||
|
|
||||||
|
let mut pos = pos.clone();
|
||||||
|
pos.x -= xoff;
|
||||||
|
if pos.y == 0 {
|
||||||
|
// main line
|
||||||
|
if pos.x >= 0 {
|
||||||
|
match self.segments.get(&(pos.x as usize)) {
|
||||||
|
Some(ListSegment::InsertCursor) => {
|
||||||
|
Some(TerminalAtom::new('|', TerminalStyle::fg_color((220, 80, 40))))
|
||||||
|
}
|
||||||
|
Some(ListSegment::Item { editor, cur_dist }) => {
|
||||||
|
if let Some(e) = editor.get_edit::<CharEditor>() {
|
||||||
|
let c = e.read().unwrap().get();
|
||||||
|
let mut style = TerminalStyle::default();
|
||||||
|
let i = if cur_dist >= 0 { pos.x as u32 } else { pos.x as u32 - 1 };
|
||||||
|
for d in self.diagnostics.iter() {
|
||||||
|
if d.range.0 <= i && d.range.1 > i {
|
||||||
|
style = TerminalStyle::bg_color((110, 80, 30))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(TerminalAtom::new(c, style))
|
||||||
|
} else {
|
||||||
|
Some(TerminalAtom::from('?'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => None
|
||||||
|
}
|
||||||
|
} else if pos.x <= -2 {
|
||||||
|
// line number
|
||||||
|
let mut n = self.line_num.get();
|
||||||
|
let digit_idx = -(pos.x+2);
|
||||||
|
for _ in 0..digit_idx { n /= 10; }
|
||||||
|
Some(
|
||||||
|
TerminalAtom::new(
|
||||||
|
char::from_digit((n % 10) as u32, 10).unwrap_or('?'),
|
||||||
|
TerminalStyle::fg_color((120,120,120))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else if pos.y > 0 {
|
||||||
|
// optional diagnostic message
|
||||||
|
let diag_idx = pos.y as usize - 1;
|
||||||
|
if let Some(diag) = self.diagnostics.get(&diag_idx) {
|
||||||
|
if let Some(c) = diag.msg.chars().nth( pos.x as usize ) {
|
||||||
|
Some(TerminalAtom::new(c,
|
||||||
|
TerminalStyle::bg_color((20,20,0))
|
||||||
|
.add(TerminalStyle::fg_color((220, 40, 40)))
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn area(&self) -> IndexArea<Point2<i16>> {
|
||||||
|
let xoff = 6;
|
||||||
|
let mut n = self.line_num.get();
|
||||||
|
let mut n_digits = 0 as i16;
|
||||||
|
while n > 0 { n_digits += 1; n /= 10; }
|
||||||
|
let diag_len = self.diagnostics.iter().map(|d| d.msg.chars().count() as i16).max().unwrap_or(0);
|
||||||
|
IndexArea::Range(
|
||||||
|
Point2::new( xoff -1-n_digits , 0) ..=
|
||||||
|
Point2::new(
|
||||||
|
xoff+
|
||||||
|
i16::max(
|
||||||
|
self.segments.len().unwrap_or(i16::MAX as usize) as i16,
|
||||||
|
diag_len
|
||||||
|
),
|
||||||
|
self.diagnostics.len().unwrap_or(i16::MAX as usize) as i16,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LineView {
|
||||||
|
pub fn new(
|
||||||
|
n: u64,
|
||||||
|
le: &ListEditor,
|
||||||
|
) -> Arc<RwLock<Self>> {
|
||||||
|
let line_num_buf = SingletonBuffer::new(n);
|
||||||
|
let diag_buf = VecBuffer::new();
|
||||||
|
let seg_seq = ListSegmentSequence::new(le.get_cursor_port(), le.get_data_port())
|
||||||
|
.read().unwrap().get_view();
|
||||||
|
|
||||||
|
let out_port = ViewPort::new();
|
||||||
|
let mut proj_helper = ProjectionHelper::new(out_port.update_hooks.clone());
|
||||||
|
|
||||||
|
let lv = Arc::new(RwLock::new(LineView{
|
||||||
|
line_num: proj_helper.new_singleton_arg(0, line_num_buf.get_port(),
|
||||||
|
|s: &mut LineView, _msg|{
|
||||||
|
s.cast.write().unwrap()
|
||||||
|
.notify(&IndexArea::Range(
|
||||||
|
(Point2::new(-100, 0) ..= Point2::new(0, 0))
|
||||||
|
));
|
||||||
|
}),
|
||||||
|
segments: proj_helper.new_sequence_arg(1, seg_seq,
|
||||||
|
|s: &mut LineView, idx| {
|
||||||
|
s.cast.write().unwrap()
|
||||||
|
.notify(&IndexArea::Range(
|
||||||
|
(Point2::new(0, *idx as i16 - 1) ..= Point2::new(100, *idx as i16))
|
||||||
|
));
|
||||||
|
}),
|
||||||
|
diagnostics: proj_helper.new_sequence_arg(2, diag_buf.get_port().to_sequence(),
|
||||||
|
|s: &mut LineView, idx| {
|
||||||
|
s.cast.write().unwrap()
|
||||||
|
.notify(&IndexArea::Range(
|
||||||
|
(Point2::new(-100, 1+*idx as i16) ..= Point2::new(100, 1+*idx as i16))
|
||||||
|
));
|
||||||
|
}),
|
||||||
|
|
||||||
|
diag_buf,
|
||||||
|
|
||||||
|
cast: out_port.inner().get_broadcast(),
|
||||||
|
proj_helper,
|
||||||
|
out_port: out_port.outer()
|
||||||
|
}));
|
||||||
|
|
||||||
|
lv.write().unwrap().proj_helper.set_proj(&lv);
|
||||||
|
out_port.inner().set_view(Some(lv.clone()));
|
||||||
|
|
||||||
|
lv
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_port(&self) -> OuterViewPort<dyn TerminalView> {
|
||||||
|
self.out_port.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LinesEditor {
|
||||||
|
ctx: Arc<RwLock<Context>>,
|
||||||
|
edit: Arc<RwLock<EditTree>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LinesEditor {
|
||||||
|
pub fn new(ctx: Arc<RwLock<Context>>) -> Self {
|
||||||
|
let typ = Context::parse(&ctx, "<List Char>");
|
||||||
|
let depth_port = SingletonBuffer::<usize>::new(0).get_port();
|
||||||
|
|
||||||
|
let list_edit = ListEditor::new(ctx.clone(), typ);
|
||||||
|
|
||||||
|
let line_to_edittree = GenericReprTreeMorphism::new(
|
||||||
|
Context::parse(&ctx, "Line ~ <Seq~List Char>"),
|
||||||
|
Context::parse(&ctx, "Line ~ EditTree"),
|
||||||
|
|rt, σ| {
|
||||||
|
eprintln!("LINE EDITOR CONSTRUCT");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
ctx.write().unwrap().morphisms.add_morphism( line_to_edittree );
|
||||||
|
|
||||||
|
let lines_segments = nested::editors::list::ListSegmentSequence::new(
|
||||||
|
list_edit.get_cursor_port(),
|
||||||
|
list_edit.get_data_port()
|
||||||
|
).read().unwrap().get_view();
|
||||||
|
let lines_view = lines_segments
|
||||||
|
.map({
|
||||||
|
let ctx = ctx.clone();
|
||||||
|
move |segment| match segment {
|
||||||
|
nested::editors::list::ListSegment::InsertCursor => {
|
||||||
|
nested_tty::make_label("..... |")
|
||||||
|
.with_fg_color((220, 80, 40))
|
||||||
|
},
|
||||||
|
nested::editors::list::ListSegment::Item { editor, cur_dist } => {
|
||||||
|
if *cur_dist == 0 {
|
||||||
|
editor.display_view()
|
||||||
|
.map_item(|x,a| a
|
||||||
|
.add_style_back(TerminalStyle::bg_color((50, 50, 50)))
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
editor.display_view()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.to_grid_vertical()
|
||||||
|
.flatten();
|
||||||
|
|
||||||
|
let mut list_edit = list_edit.into_node( depth_port );
|
||||||
|
nested_tty::editors::list::PTYListController::for_node( &mut list_edit, Some('\n'), None );
|
||||||
|
list_edit.disp.view
|
||||||
|
.write().unwrap()
|
||||||
|
.insert_branch(ReprTree::from_view(
|
||||||
|
Context::parse(&ctx, "TerminalView"),
|
||||||
|
lines_view
|
||||||
|
));
|
||||||
|
|
||||||
|
LinesEditor {
|
||||||
|
// lines,
|
||||||
|
edit: Arc::new(RwLock::new(list_edit)),
|
||||||
|
ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_line(&mut self, line_value: &str) {
|
||||||
|
let n = self.edit.write().unwrap()
|
||||||
|
.get_edit::< ListEditor >().unwrap()
|
||||||
|
.read().unwrap()
|
||||||
|
.data.len() as u64;
|
||||||
|
|
||||||
|
let line =
|
||||||
|
self.make_line(line_value)
|
||||||
|
.descend(Context::parse(&self.ctx, "EditTree")).unwrap()
|
||||||
|
.edittree(&self.ctx).get();
|
||||||
|
|
||||||
|
let le = line.read().unwrap().get_edit::<ListEditor>().unwrap();
|
||||||
|
le.write().unwrap().goto(TreeCursor::none());
|
||||||
|
|
||||||
|
let lvport = LineView::new( n, &*le.read().unwrap() ).read().unwrap().get_port();
|
||||||
|
line.write().unwrap().disp.view
|
||||||
|
.insert_leaf(
|
||||||
|
Context::parse(&self.ctx, "TerminalView"),
|
||||||
|
ReprLeaf::from_view( lvport )
|
||||||
|
);
|
||||||
|
|
||||||
|
self.edit.write().unwrap()
|
||||||
|
.get_edit::< ListEditor >().unwrap()
|
||||||
|
.write().unwrap()
|
||||||
|
.data
|
||||||
|
.push(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_line(&self, line_value: &str) -> Arc<RwLock<ReprTree>> {
|
||||||
|
let ctx = &self.ctx;
|
||||||
|
let mut rt_line = ReprTree::from_str(
|
||||||
|
Context::parse(&ctx, "<List Char>~<Vec Char>"),
|
||||||
|
line_value
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.read().unwrap().apply_morphism(
|
||||||
|
&rt_line,
|
||||||
|
&laddertypes::MorphismType {
|
||||||
|
src_type: Context::parse(&ctx, "<List~Vec Char>"),
|
||||||
|
dst_type: Context::parse(&ctx, "<List Char> ~ EditTree")
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// .. avoid cycle of projections..
|
||||||
|
rt_line.write().unwrap().detach(&ctx);
|
||||||
|
|
||||||
|
ctx.read().unwrap().apply_morphism(
|
||||||
|
&rt_line,
|
||||||
|
&laddertypes::MorphismType {
|
||||||
|
src_type: Context::parse(&ctx, "<List Char>~EditTree"),
|
||||||
|
dst_type: Context::parse(&ctx, "<List Char>")
|
||||||
|
}
|
||||||
|
);
|
||||||
|
ctx.read().unwrap().apply_morphism(
|
||||||
|
&rt_line,
|
||||||
|
&laddertypes::MorphismType {
|
||||||
|
src_type: Context::parse(&ctx, "<List Char>"),
|
||||||
|
dst_type: Context::parse(&ctx, "<List Char>~<Vec Char>")
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
rt_line
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_diagnostic(&mut self, line_num: usize, diag: LineDiagnostic) {
|
||||||
|
/*
|
||||||
|
let mut line = self.edit.write().unwrap()
|
||||||
|
.get_edit::< ListEditor >().expect("cant get list edit")
|
||||||
|
.write().unwrap()
|
||||||
|
.data
|
||||||
|
.get(line_num)
|
||||||
|
.get_edit::< LineEditor >().expect("cant get line edit");
|
||||||
|
|
||||||
|
line.write().unwrap().diag_buf.push(diag);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
pub fn get_lines(&self) -> Vec<String> {
|
||||||
|
// TODO
|
||||||
|
let lines = self.edit.read().unwrap().get_edit::< ListEditor >().expect("cant get list edit")
|
||||||
|
.read().unwrap().data
|
||||||
|
.clone().into_inner()
|
||||||
|
.read().unwrap()
|
||||||
|
.iter()
|
||||||
|
.map(|line_edittree_arc| {
|
||||||
|
let line_edittree = line_edittree_arc.read().unwrap();
|
||||||
|
line_edittree.
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self) -> String {
|
||||||
|
self.get_lines().iter()
|
||||||
|
.map(|l|
|
||||||
|
l.chars().chain(std::iter::once('\n'))
|
||||||
|
).flatten()
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[async_std::main]
|
||||||
|
async fn main() {
|
||||||
|
/* setup context
|
||||||
|
*/
|
||||||
|
let ctx = Arc::new(RwLock::new(Context::new()));
|
||||||
|
nested::editors::char::init_ctx( ctx.clone() );
|
||||||
|
nested::editors::digit::init_ctx( ctx.clone() );
|
||||||
|
nested::editors::integer::init_ctx( ctx.clone() );
|
||||||
|
nested::editors::list::init_ctx( ctx.clone() );
|
||||||
|
nested_tty::setup_edittree_hook(&ctx);
|
||||||
|
|
||||||
|
let args: Vec<String> = std::env::args().collect();
|
||||||
|
let path = String::from(args.get(1).expect("no filename given"));
|
||||||
|
let mut lines_edit = LinesEditor::new(ctx.clone());
|
||||||
|
|
||||||
|
let iter_lines = iterate_text::file::lines::IterateFileLines::new(path.clone());
|
||||||
|
for line in iter_lines {
|
||||||
|
let mut sanitized_line = String::new();
|
||||||
|
for c in line.chars() {
|
||||||
|
if c == '\t' {
|
||||||
|
for _ in 0..4 { sanitized_line.push(' '); }
|
||||||
|
} else if c != '\n' {
|
||||||
|
sanitized_line.push(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lines_edit.add_line(&sanitized_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
lines_edit.edit.write().unwrap().goto(TreeCursor::home());
|
||||||
|
lines_edit.add_diagnostic( 5, LineDiagnostic{ range: (0, 10), msg: "test diagnostic".into() } );
|
||||||
|
|
||||||
|
/* setup terminal
|
||||||
|
*/
|
||||||
|
let app = TTYApplication::new({
|
||||||
|
let edittree = lines_edit.edit.clone();
|
||||||
|
|
||||||
|
/* event handler
|
||||||
|
*/
|
||||||
|
let ctx = ctx.clone();
|
||||||
|
move |ev| {
|
||||||
|
edittree.write().unwrap().send_cmd_obj(ev.to_repr_tree(&ctx));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Setup the compositor to serve as root-view
|
||||||
|
* by routing it to the `app.port` Viewport,
|
||||||
|
* so it will be displayed on TTY-output.
|
||||||
|
*/
|
||||||
|
let compositor = TerminalCompositor::new(app.port.inner());
|
||||||
|
|
||||||
|
/* Now add some views to our compositor
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
let mut comp = compositor.write().unwrap();
|
||||||
|
let edit = lines_edit.edit.read().unwrap();
|
||||||
|
comp.push(edit.get_cursor_widget());
|
||||||
|
comp.push(edit.display_view().offset(Vector2::new(0, 1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* write the changes in the view of `term_port` to the terminal
|
||||||
|
*/
|
||||||
|
app.show().await.expect("output error!");
|
||||||
|
}
|
Loading…
Reference in a new issue