A simple CPU rendered GUI IDE experience.
check mtime
| -rw-r--r-- | src/bar.rs | 16 | ||||
| -rw-r--r-- | src/main.rs | 89 | ||||
| -rw-r--r-- | src/text.rs | 3 |
3 files changed, 91 insertions, 17 deletions
@@ -52,7 +52,7 @@ impl Bar { .zip(self.last_action.chars().rev()) .for_each(|(x, y)| x.letter = Some(y)); } - State::Procure((x, r)) => { + State::Procure(x, r) => { r.prompt() .chars() .zip(repeat(Style::BOLD | Style::ITALIC)) @@ -65,6 +65,18 @@ impl Bar { } }); } + State::RequestBoolean(x) => { + x.prompt() + .chars() + .zip(repeat(Style::BOLD | Style::ITALIC)) + .zip(row) + .for_each(|((x, z), y)| { + *y = Cell { + letter: Some(x), + style: Style { flags: z, ..y.style }, + } + }); + } State::Selection(x) => { let [(x1, y1), (x2, y2)] = t.position(x.clone()); format!("selection from ({x1}, {y1}) to ({x2}, {y2})") @@ -73,7 +85,7 @@ impl Bar { .zip(row.iter_mut().rev()) .for_each(|(x, y)| y.letter = Some(x)); } - State::Search((x, y, z)) => { + State::Search(x, y, z) => { format!("{} ({} of {z})", x.as_str(), y + 1) .chars() .zip(row) diff --git a/src/main.rs b/src/main.rs index 07170b6..23f3334 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,7 @@ #![allow(incomplete_features, redundant_semicolons)] use std::convert::identity; use std::num::NonZeroU32; +use std::path::PathBuf; use std::sync::LazyLock; use std::time::Instant; @@ -22,6 +23,7 @@ use dsb::cell::Style; use dsb::{Cell, F}; use fimg::Image; use regex::Regex; +use ropey::Rope; use rust_fsm::StateMachineImpl; use swash::{FontRef, Instance}; use winit::event::{ @@ -121,7 +123,8 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { let ls = 20.0; let mut text = TextArea::default(); - let mut origin = std::env::args().nth(1); + let mut origin = + std::env::args().nth(1).and_then(|x| PathBuf::try_from(x).ok()); let mut fonts = dsb::Fonts::new( F::FontRef(*FONT, &[(2003265652, 550.0)]), F::instance(*FONT, *BFONT), @@ -146,6 +149,14 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { last_edit: Instant::now(), changed: false, }; + macro_rules! modify { + () => { + origin + .as_ref() + .map(|x| x.metadata().unwrap().modified().unwrap()) + }; + } + let mut mtime = modify!(); macro_rules! save { () => {{ std::fs::write( @@ -154,6 +165,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { ) .unwrap(); bar.last_action = "saved".into(); + mtime = modify!(); }}; } let app = winit_app::WinitAppBuilder::with_init( @@ -184,6 +196,10 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { window.inner_size().height as _, ), ); + if modify!() != mtime { + mtime = modify!(); + state.consume(Action::Changed).unwrap(); + } match event { Event::WindowEvent { window_id, @@ -256,7 +272,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { (t_ox, 0), x, |(c, r), text, x| { - if let State::Search((re, j, _)) = &state { + if let State::Search(re, j, _) = &state { re.find_iter(&text.rope.to_string()) .enumerate() .for_each(|(i, m)| { @@ -288,7 +304,10 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { FG, (&mut cells, (c, r)), r - 1, - origin.as_deref().unwrap_or("new buffer"), + origin + .as_ref() + .map(|x| x.to_str().unwrap()) + .unwrap_or("new buffer"), &state, &text, ); @@ -506,7 +525,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { } }, Some(Do::SaveTo(x)) => { - origin = Some(x); + origin = Some(PathBuf::try_from(x).unwrap()); save!(); } Some(Do::Edit) => { @@ -543,7 +562,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { .extend_selection(y, state.sel().clone()); text.scroll_to_cursor(); } - Some(Do::Insert((x, c))) => { + Some(Do::Insert(x, c)) => { hist.push_if_changed(&text); text.rope.remove(x.clone()); text.cursor = x.start; @@ -575,7 +594,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { hist.push_if_changed(&text); } Some(Do::OpenFile(x)) => { - origin = Some(x.clone()); + origin = Some(PathBuf::try_from(&x).unwrap()); text = TextArea::default(); text.insert( &std::fs::read_to_string(x).unwrap(), @@ -605,7 +624,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { .enumerate() .find(|(_, x)| x.start() > text.cursor) .map(|(x, m)| { - state = State::Search((s, x, n)); + state = State::Search(s, x, n); text.cursor = text.rope.byte_to_char(m.end()); text.scroll_to_cursor_centering(); @@ -621,6 +640,25 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { text.cursor = text.rope.byte_to_char(m.end()); text.scroll_to_cursor_centering(); } + Some(Do::Boolean( + BoolRequest::ReloadFile, + true, + )) => { + text.rope = Rope::from_str( + &std::fs::read_to_string( + origin.as_ref().unwrap(), + ) + .unwrap(), + ); + text.cursor = + text.cursor.min(text.rope.len_chars()); + mtime = modify!(); + bar.last_action = "reloaded".into(); + } + Some(Do::Boolean( + BoolRequest::ReloadFile, + false, + )) => {} None => {} } window.request_redraw(); @@ -705,9 +743,9 @@ impl State { let State::Selection(x) = self else { panic!() }; x } - fn search(&mut self) -> &mut (Regex, usize, usize) { - let State::Search(x) = self else { panic!() }; - x + fn search(&mut self) -> (&mut Regex, &mut usize, &mut usize) { + let State::Search(x, y, z) = self else { panic!() }; + (x, y, z) } } @@ -730,10 +768,11 @@ Default => { K(Key::Named(ArrowUp | ArrowLeft | ArrowDown | ArrowRight | Home | End) if shift()) => Selection(Range<usize> => 0..0) [StartSelection], M(MouseButton => MouseButton::Left if shift()) => Selection(Range<usize> => 0..0) [StartSelection], M(MouseButton => MouseButton::Left) => Default [MoveCursor], - C((usize, usize) => _ if unsafe { CLICKING }) => Selection(0..0) [StartSelection], - C(_) => Default, - K(_) => Default [Edit], - M(_) => Default, + C(((usize, usize)) => .. if unsafe { CLICKING }) => Selection(0..0) [StartSelection], + Changed => RequestBoolean(BoolRequest => BoolRequest::ReloadFile), + C(_) => _, + K(_) => _ [Edit], + M(_) => _, }, Selection(x if shift()) => { K(Key::Named(ArrowUp | ArrowLeft | ArrowDown | ArrowRight | Home | End)) => Selection(x) [UpdateSelection], @@ -758,11 +797,19 @@ Procure((t, InputRequest::Search)) => K(Key::Named(Enter)) => Default [StartSear Procure((t, InputRequest::SaveFile)) => K(Key::Named(Enter)) => Default [SaveTo(String => t.rope.to_string())], Procure((t, InputRequest::OpenFile)) => K(Key::Named(Enter)) => Default [OpenFile(String => t.rope.to_string())], Procure((t, a)) => K(k) => Procure((handle(k, t), a)), +RequestBoolean(t) => { + K(Key::Character(x) if x == "y") => Default [Boolean((BoolRequest, bool) => (t, true))], + K(Key::Character(x) if x == "n") => Default [Boolean((t, false))], + K(Key::Named(Escape)) => Default [Boolean((t, false))], + K(_) => RequestBoolean(t), + C(_) => _, + Changed => _, +}, Search((x, y, m)) => { M(MouseButton => MouseButton::Left) => Default [MoveCursor], C(_) => Search((x, y, m)), K(Key::Named(Enter) if shift()) => Search((x, y.checked_sub(1).unwrap_or(m-1), m)) [SearchChanged], - K(Key::Named(Enter)) => Search((Regex, usize, usize) => (x, (y+ 1) % m, m)) [SearchChanged], + K(Key::Named(Enter)) => Search((Regex, usize, usize) => (x, (y+ 1) % m, m)) [SearchChanged], K(_) => Default [Reinsert], } } @@ -782,6 +829,18 @@ impl InputRequest { } } } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum BoolRequest { + ReloadFile, +} +impl BoolRequest { + fn prompt(self) -> &'static str { + match self { + BoolRequest::ReloadFile => "file changed. reload? y/n.", + } + } +} + #[test] fn history_test() { let mut t = TextArea::default(); diff --git a/src/text.rs b/src/text.rs index 19b5943..3f187dd 100644 --- a/src/text.rs +++ b/src/text.rs @@ -31,6 +31,9 @@ const STYLES: [Option<u8>; 13] = amap::amap! { 12 => 0, }; + + + const fn color(x: &[u8; 6]) -> [u8; 3] { car::map!( car::map!(x, |b| (b & 0xF) + 9 * (b >> 6)).chunked::<2>(), |