A simple CPU rendered GUI IDE experience.
use pattern matching on smolstr
| -rw-r--r-- | Cargo.toml | 8 | ||||
| -rw-r--r-- | src/commands.rs | 2 | ||||
| -rw-r--r-- | src/edi.rs | 55 | ||||
| -rw-r--r-- | src/edi/st.rs | 44 | ||||
| -rw-r--r-- | src/lsp.rs | 91 | ||||
| -rw-r--r-- | src/main.rs | 138 | ||||
| -rw-r--r-- | src/rnd.rs | 99 | ||||
| -rw-r--r-- | src/winit_app.rs | 237 |
8 files changed, 420 insertions, 254 deletions
@@ -11,7 +11,7 @@ implicit-fn = "0.1.0" ropey = "1.6.1" softbuffer = "0.4.6" swash = "0.2.5" -winit = "0.30.12" +winit = "0.31.0-beta.2" tree-sitter = "0.25.0" car = "0.1.2" @@ -68,7 +68,7 @@ vecto = "0.1.1" rangemap = { version = "1.7.1", features = ["const_fn", "nightly", "serde1"] } itern = "0.1.1" kitty-rc = { version = "0.4.2", git = "https://github.com/bend-n/kitty-rc-rs" } - +smol_str = "0.3.6" [profile.dev.package] rust-analyzer.opt-level = 3 fimg.opt-level = 3 @@ -88,3 +88,7 @@ debug-assertions = true [profile.dev] incremental = false + +[patch.crates-io] +smol_str = { git = "https://git.bendn.org/rust-analyzer" } +# winit = { git = "https://github.com/rust-windowing/winit" } diff --git a/src/commands.rs b/src/commands.rs index fdd4efa..aed4a00 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -193,7 +193,7 @@ impl Editor { pub fn handle_command( &mut self, z: Cmd, - w: Arc<winit::window::Window>, + w: Arc<dyn winit::window::Window>, ) -> anyhow::Result<()> { match z { Cmd::GoTo(Some(x)) => @@ -24,6 +24,7 @@ use tokio_util::task::AbortOnDropHandle as DropH; use winit::event::{KeyEvent, MouseButton}; use winit::keyboard::{Key, NamedKey}; use winit::window::Window; + pub mod st; use st::*; @@ -142,7 +143,7 @@ pub struct Editor { pub lsp: Option<( &'static Client, std::thread::JoinHandle<()>, - Option<Sender<Arc<Window>>>, + Option<Sender<Arc<dyn Window>>>, )>, // #[serde(skip)] pub requests: Requests, @@ -192,7 +193,6 @@ macro_rules! change { x.rq_semantic_tokens( &mut $self.requests.semantic_tokens, origin, - $w, ) .unwrap(); inlay!($self); @@ -343,7 +343,6 @@ impl Editor { c.rq_semantic_tokens( &mut me.requests.semantic_tokens, origin, - None, ) .unwrap() }, @@ -414,7 +413,7 @@ impl Editor { std::fs::write(self.origin.as_ref().unwrap(), &t).unwrap(); self.mtime = Self::modify(self.origin.as_deref()); } - pub fn poll(&mut self, w: Option<&Arc<Window>>) { + pub fn poll(&mut self, w: Option<&Arc<dyn Window>>) { let Some((l, ..)) = self.lsp else { return }; for rq in l.req_rx.try_iter() { match rq { @@ -468,7 +467,7 @@ impl Editor { ); } State::CodeAction(x) => { - x.poll_r( + if x.poll_r( |x, _| { let lems: Vec<CodeAction> = x .ok()?? @@ -483,13 +482,15 @@ impl Editor { "no code actions available".into(); None } else { + self.bar.last_action = + format!("{} code actions", lems.len()); Some(act::CodeActions::new(lems)) } }, r, w, - ); - if x.result.is_none() { + ) && x.result.is_none() + { self.state = State::Default; } } @@ -555,7 +556,7 @@ impl Editor { pub fn cursor_moved( &mut self, cursor_position: (usize, usize), - w: Arc<Window>, + w: Arc<dyn Window>, c: usize, ) { match self.state.consume(Action::C(cursor_position)).unwrap() { @@ -793,7 +794,7 @@ impl Editor { &mut self, bt: MouseButton, cursor_position: (usize, usize), - w: Arc<Window>, + w: Arc<dyn Window>, ) { let text = &mut self.text; _ = self @@ -900,12 +901,13 @@ impl Editor { pub fn keyboard( &mut self, event: KeyEvent, - window: &mut Arc<Window>, + window: &mut Arc<dyn Window>, ) -> ControlFlow<()> { let mut o: Option<Do> = self .state .consume(Action::K(event.logical_key.clone())) .unwrap(); + match o { Some(Do::Reinsert) => o = self @@ -1251,12 +1253,7 @@ impl Editor { self.hist.lc = self.text.cursor.clone(); self.hist.test_push(&mut self.text); let act = lsp - .runtime - .block_on( - lsp.request::<CodeActionResolveRequest>(&act) - .unwrap() - .0, - ) + .request_immediate::<CodeActionResolveRequest>(&act) .unwrap(); let f = f.to_owned(); act.edit.map(|x| self.apply_wsedit(x, &f)); @@ -1395,7 +1392,6 @@ impl Editor { change!(self, window.clone()); } lsp!(self + p).map(|(lsp, o)| { - let window = window.clone(); match event.logical_key.as_ref() { Key::Character(y) if let Some(x) = &lsp.initialized @@ -1839,7 +1835,7 @@ impl Editor { pub fn open( &mut self, x: &Path, - w: Arc<Window>, + w: Arc<dyn Window>, ) -> anyhow::Result<()> { let x = x.canonicalize()?.to_path_buf(); if Some(&*x) == self.origin.as_deref() { @@ -1872,9 +1868,9 @@ impl Editor { lsp: Option<( &'static Client, std::thread::JoinHandle<()>, - Option<Sender<Arc<Window>>>, + Option<Sender<Arc<dyn Window>>>, )>, - w: Option<Arc<Window>>, + w: Option<Arc<dyn Window>>, ws: Option<PathBuf>, ) -> anyhow::Result<()> { if let Some(x) = self.files.remove(x) { @@ -1928,7 +1924,6 @@ impl Editor { x.rq_semantic_tokens( &mut self.requests.semantic_tokens, origin, - w.clone(), ) .unwrap(); }); @@ -1936,7 +1931,7 @@ impl Editor { self.set_title(w); Ok(()) } - pub fn set_title(&self, w: Option<Arc<Window>>) { + pub fn set_title(&self, w: Option<Arc<dyn Window>>) { if let Some(x) = w && let Some(t) = self.title() { @@ -1983,7 +1978,7 @@ impl Editor { pub fn open_loc( &mut self, Location { uri, range }: &Location, - w: Arc<Window>, + w: Arc<dyn Window>, ) { self.open(&uri.to_file_path().unwrap(), w.clone()).unwrap(); @@ -1996,7 +1991,7 @@ impl Editor { pub fn open_loclink( &mut self, LocationLink { target_uri, target_range, .. }: &LocationLink, - w: Arc<Window>, + w: Arc<dyn Window>, ) { self.open(&target_uri.to_file_path().unwrap(), w.clone()).unwrap(); @@ -2017,7 +2012,7 @@ pub fn handle2<'a>( use Key::*; match key { - Named(Space) => text.insert(" "), + Character(" ") => text.insert(" "), Named(Backspace) if ctrl() => text.backspace_word(), Named(Backspace) => text.backspace(), Named(Home) if ctrl() => { @@ -2035,10 +2030,12 @@ pub fn handle2<'a>( text.right(); text.backspace() } - Character(x) if x == "a" && alt() => text.left(), - Character(x) if x == "w" && alt() => text.up(), - Character(x) if x == "s" && alt() => text.down(), - Character(x) if x == "d" && alt() => text.right(), + Character("d") if alt() && ctrl() => text.word_left(), + Character("a") if alt() && ctrl() => text.word_right(), + Character("a") if alt() => text.left(), + Character("w") if alt() => text.up(), + Character("s") if alt() => text.down(), + Character("d") if alt() => text.right(), Named(ArrowLeft) if ctrl() => text.word_left(), Named(ArrowRight) if ctrl() => text.word_right(), Named(ArrowLeft) => text.left(), diff --git a/src/edi/st.rs b/src/edi/st.rs index c6fbdbb..5c7568f 100644 --- a/src/edi/st.rs +++ b/src/edi/st.rs @@ -38,20 +38,20 @@ pub(crate) State => #[derive(Debug)] pub(crate) Action => #[derive(Debug)] pub(c Dead => K(Key => _) => Dead, Default => { - K(Key::Character(x) if x == "s" && ctrl()) => Save [Save], - K(Key::Character(x) if x == "q" && ctrl()) => Dead [Quit], - K(Key::Character(x) if x == "v" && ctrl()) => _ [Paste], - K(Key::Character(x) if x == "z" && ctrl()) => _ [Undo], - K(Key::Character(x) if x == "y" && ctrl()) => _ [Redo], - K(Key::Character(x) if x == "f" && ctrl()) => Procure((default(), InputRequest::Search)), - K(Key::Character(x) if x == "o" && ctrl()) => Procure((default(), InputRequest::OpenFile)), - K(Key::Character(x) if x == "c" && ctrl()) => _, - K(Key::Character(x) if x == "l" && ctrl()) => _ [Symbols], - K(Key::Character(x) if x == "." && ctrl()) => _ [CodeAction], - K(Key::Character(x) if x == "0" && ctrl()) => _ [MatchingBrace], - K(Key::Character(x) if x == "`" && ctrl()) => _ [SpawnTerminal], - K(Key::Character(y) if y == "/" && ctrl()) => Default [Comment(State => State::Default)], - K(Key::Character(x) if x == "p" && ctrl()) => Command(Commands => Commands::default()), + K(Key::Character("s") if ctrl()) => Save [Save], + K(Key::Character("q") if ctrl()) => Dead [Quit], + K(Key::Character("v") if ctrl()) => _ [Paste], + K(Key::Character("z") if ctrl()) => _ [Undo], + K(Key::Character("y") if ctrl()) => _ [Redo], + K(Key::Character("f") if ctrl()) => Procure((default(), InputRequest::Search)), + K(Key::Character("o") if ctrl()) => Procure((default(), InputRequest::OpenFile)), + K(Key::Character("c") if ctrl()) => _, + K(Key::Character("l") if ctrl()) => _ [Symbols], + K(Key::Character(".") if ctrl()) => _ [CodeAction], + K(Key::Character("0") if ctrl()) => _ [MatchingBrace], + K(Key::Character("`") if ctrl()) => _ [SpawnTerminal], + K(Key::Character("/") if ctrl()) => Default [Comment(State => State::Default)], + K(Key::Character("p") if ctrl()) => Command(Commands => Commands::default()), K(Key::Named(Backspace) if alt()) => _ [DeleteBracketPair], K(Key::Named(F1)) => Procure((default(), InputRequest::RenameSymbol)), K(Key::Named(k @ (ArrowUp | ArrowDown)) if alt()) => _ [InsertCursor(Direction => { @@ -62,8 +62,8 @@ Default => { M(MouseButton::Left if alt()) => _ [InsertCursorAtMouse], M(MouseButton::Left if ctrl()) => _ [GoToDefinition], M(MouseButton::Left) => _ [MoveCursor], - K(Key::Character(x) if x == "-" && ctrl()) => _ [NavBack], - K(Key::Character(x) if x == "=" && ctrl()) => _ [NavBack], + K(Key::Character("=") if ctrl()) => _ [NavForward], + K(Key::Character("-") if ctrl()) => _ [NavBack], M(MouseButton::Back) => _ [NavBack], M(MouseButton::Forward) => _ [NavForward], C(((usize, usize)) => .. if unsafe { CLICKING }) => Selection [StartSelection], @@ -108,7 +108,7 @@ Symbols(Rq { result: Some(_x), request: _rq }) => { K(Key::Named(Escape)) => Default, }, Symbols(Rq::<Symbols, Option<SymbolsList>, (), AQErr> => _rq) => { - K(Key::Character(x) if x == "d" && ctrl()) => _ [SwitchType], // crahs cond methinks + K(Key::Character("d") if ctrl()) => _ [SwitchType], // crahs cond methinks K(Key::Named(Escape)) => Default, K(_) => _ [SymbolsHandleKey], C(_) => _, @@ -136,9 +136,9 @@ Selection => { C(_) => _, M(MouseButton => MouseButton::Left) => Default [MoveCursor], K(Key::Named(Backspace)) => Default [Delete], - K(Key::Character(y) if y == "x" && ctrl()) => Default [Cut], - K(Key::Character(y) if y == "c" && ctrl()) => Default [Copy], - K(Key::Character(y) if y == "/" && ctrl()) => Default [Comment(State::Selection)], + K(Key::Character("x") if ctrl()) => Default [Cut], + K(Key::Character("c") if ctrl()) => Default [Copy], + K(Key::Character("/") if ctrl()) => Default [Comment(State::Selection)], M(_) => _, K(Key::Character(y) if !ctrl()) => Default [Insert(SmolStr => y)], @@ -158,8 +158,8 @@ Procure((t, InputRequest::RenameSymbol)) => K(Key::Named(Enter)) => Default [Ren Procure((t, a)) => K(k) => Procure((handle(k, t), a)), Procure((t, a)) => C(_) => Procure((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::Character("n")) => Default [Boolean((BoolRequest, bool) => (t, true))], + K(Key::Character("y")) => Default [Boolean((t, false))], K(Key::Named(Escape)) => Default [Boolean((t, false))], K(_) => RequestBoolean(t), C(_) => _, @@ -42,7 +42,7 @@ pub struct Client { pub id: AtomicI32, pub initialized: Option<InitializeResult>, // pub pending: HashMap<i32, oneshot::Sender<Re>>, - pub send_to: Sender<(i32, oneshot::Sender<Re>)>, + pub send_to: Sender<(i32, oneshot::Sender<Re>, BehaviourAfter)>, pub progress: &'static papaya::HashMap< ProgressToken, Option<(WorkDoneProgress, WorkDoneProgressBegin)>, @@ -138,9 +138,9 @@ impl Client { &'me self, y: &X::Params, ) -> Result<X::Result, RequestError<X>> { - self.runtime.block_on(self.request::<X>(y)?.0) + self.runtime.block_on(self.request_::<X, { Nil }>(y)?.0) } - #[must_use] + pub fn request<'me, X: Request>( &'me self, y: &X::Params, @@ -152,6 +152,20 @@ impl Client { ), SendError<Message>, > { + self.request_::<X, { Redraw }>(y) + } + #[must_use] + fn request_<'me, X: Request, const THEN: BehaviourAfter>( + &'me self, + y: &X::Params, + ) -> Result< + ( + impl Future<Output = Result<X::Result, RequestError<X>>> + + use<'me, X, THEN>, + i32, + ), + SendError<Message>, + > { let id = self.id.fetch_add(1, std::sync::atomic::Ordering::AcqRel); self.tx.send(Message::Request(LRq { id: id.into(), @@ -161,7 +175,7 @@ impl Client { let (tx, rx) = oneshot::channel(); if self.initialized.is_some() { debug!("sent request {} ({id})'s handler", X::METHOD); - self.send_to.send((id, tx)).expect("oughtnt really fail"); + self.send_to.send((id, tx, THEN)).expect("oughtnt really fail"); } Ok(( async move { @@ -268,7 +282,7 @@ impl Client { >, > + use<'me> { let (rx, _) = self - .request::<Completion>(&CompletionParams { + .request_::<Completion, { Redraw }>(&CompletionParams { text_document_position: TextDocumentPositionParams { text_document: f.tid(), position: Position { line: y as _, character: x as _ }, @@ -291,7 +305,7 @@ impl Client { RequestError<SignatureHelpRequest>, >, > + use<'me> { - self.request::<SignatureHelpRequest>(&SignatureHelpParams { + self.request_::<SignatureHelpRequest, { Redraw }>(&SignatureHelpParams { context: None, text_document_position_params: TextDocumentPositionParams { text_document: f.tid(), @@ -431,7 +445,7 @@ impl Client { work_done_progress_params: default(), partial_result_params: default(), }; - self.request::<lsp_request!("textDocument/documentHighlight")>(&p) + self.request_::<lsp_request!("textDocument/documentHighlight"), {Redraw}>(&p) .unwrap() .0 .map(|x| x.map(|x| x.unwrap_or_default())) @@ -445,7 +459,7 @@ impl Client { RequestError<lsp_request!("textDocument/documentSymbol")>, >, > { - self.request::<lsp_request!("textDocument/documentSymbol")>( + self.request_::<lsp_request!("textDocument/documentSymbol"), { Redraw }>( &DocumentSymbolParams { text_document: p.tid(), work_done_progress_params: default(), @@ -464,7 +478,7 @@ impl Client { RequestError<lsp_request!("workspace/symbol")>, >, > { - self.request::<lsp_request!("workspace/symbol")>( + self.request_::<lsp_request!("workspace/symbol"), {Redraw}>( &lsp_types::WorkspaceSymbolParams { query: f, search_scope: Some( @@ -523,7 +537,7 @@ impl Client { RequestError<lsp_request!("textDocument/inlayHint")>, >, > + use<> { - self.request::<lsp_request!("textDocument/inlayHint")>(&InlayHintParams { + self.request_::<lsp_request!("textDocument/inlayHint"), { Redraw }>(&InlayHintParams { work_done_progress_params: default(), text_document: f.tid(), range: t.to_l_range(lower::saturating::math!{ @@ -577,7 +591,6 @@ impl Client { RequestError<SemanticTokensFullRequest>, >, f: &Path, - w: Option<Arc<Window>>, ) -> anyhow::Result<()> { debug!("requested semantic tokens"); @@ -585,7 +598,7 @@ impl Client { else { return Ok(()); }; - let (rx, _) = self.request::<SemanticTokensFullRequest>( + let (rx, _) = self.request_::<SemanticTokensFullRequest, {Redraw}>( &SemanticTokensParams { work_done_progress_params: default(), partial_result_params: default(), @@ -602,7 +615,6 @@ impl Client { SemanticTokensResult::Tokens(x) => x.data.into_boxed_slice(), }; - w.map(|x| x.request_redraw()); Ok(r) }); to.request(x); @@ -612,17 +624,11 @@ impl Client { pub fn enter<'a>(&self, f: &Path, t: &'a mut TextArea) { ceach!(t.cursor, |c| { - let r = self - .runtime - .block_on( - self.request::<OnEnter>(&TextDocumentPositionParams { + let r = self.request_immediate::<OnEnter>(&TextDocumentPositionParams { text_document: f.tid(), position: t.to_l_position(*c).unwrap(), }) - .unwrap() - .0, - ) - .unwrap(); + .unwrap(); match r { None => t.enter(), Some(mut r) => { @@ -653,13 +659,14 @@ impl Client { pub fn run( (tx, rx): (Sender<Message>, Receiver<Message>), workspace: WorkspaceFolder, -) -> (Client, std::thread::JoinHandle<()>, oneshot::Sender<Arc<Window>>) { +) -> (Client, std::thread::JoinHandle<()>, oneshot::Sender<Arc<dyn Window>>) +{ let now = Instant::now(); let (req_tx, req_rx) = unbounded(); let (not_tx, not_rx) = unbounded(); let (_req_tx, _req_rx) = unbounded(); - let (window_tx, window_rx) = oneshot::channel::<Arc<Window>>(); - let mut c: Client = Client { + let (window_tx, window_rx) = oneshot::channel::<Arc<dyn Window>>(); + let mut c = Client { tx, progress: Box::leak(Box::new(papaya::HashMap::new())), runtime: tokio::runtime::Builder::new_multi_thread() @@ -965,9 +972,9 @@ pub fn run( loop { crossbeam::select! { recv(req_rx) -> x => match x { - Ok((x, y)) => { + Ok((x, y, and)) => { debug!("received request {x}"); - assert!(map.insert(x, (y, Instant::now())).is_none()); + assert!(map.insert(x, (y, Instant::now(), and)).is_none()); } Err(RecvError) => return, }, @@ -994,20 +1001,22 @@ pub fn run( if let Some(e) = &x.error { if e.code == ErrorCode::RequestCanceled as i32 {} else if e.code == ErrorCode::ServerCancelled as i32 { - if let Some((s, _)) = map.remove(&x.id.i32()) { + if let Some((s, _, t)) = map.remove(&x.id.i32()) { log::info!("request {} cancelled", x.id); _ = s.send(x); + if t == Redraw { w.request_redraw() } } } else { - if let Some((s, _)) = map.remove(&x.id.i32()) { + if let Some((s, _, t)) = map.remove(&x.id.i32()) { _ = s.send(x.clone()); + if t == Redraw { w.request_redraw() } trace!("received error from lsp for response {x:?}"); } else { error!("received error from lsp for response {x:?}"); } } } - else if let Some((s, took)) = map.remove(&x.id.i32()) { + else if let Some((s, took, t)) = map.remove(&x.id.i32()) { log::info!("request {} took {:?}", x.id, took.elapsed()); match s.send(x) { Ok(()) => {} @@ -1017,6 +1026,9 @@ pub fn run( ); } } + // w.request_redraw(); + dbg!(t); + if t == Redraw { w.request_redraw() } } else { error!("request {x:?} was dropped.") } @@ -1056,7 +1068,13 @@ pub fn run( }); (c, h, window_tx) } - +#[derive(Copy, Clone, PartialEq, Eq, std::marker::ConstParamTy, Debug)] +pub enum BehaviourAfter { + Redraw, + // Poll, ? how impl. + Nil, +} +pub use BehaviourAfter::*; // trait RecvEepy<T>: Sized { // fn recv_eepy(self) -> Result<T, RecvError> { // self.recv_sleepy(100) @@ -1177,7 +1195,7 @@ impl<T, R, D, E> Rq<T, R, D, E> { &mut self, f: impl FnOnce(Result<R, E>, (D, Option<T>)) -> Option<T>, runtime: &tokio::runtime::Runtime, - ) { + ) -> bool { if self.request.as_mut().is_some_and(|(x, _)| x.is_finished()) && let Some((task, d)) = self.request.take() { @@ -1187,23 +1205,24 @@ impl<T, R, D, E> Rq<T, R, D, E> { log::error!( "unexpected join error from request poll: {e}" ); - return; + return false; } }; self.result = f(x, (d, self.result.take())); - } + true + } else { false } } pub fn poll_r( &mut self, f: impl FnOnce(Result<R, E>, (D, Option<T>)) -> Option<T>, runtime: &tokio::runtime::Runtime, - w: Option<&Arc<Window>>, - ) { + _w: Option<&Arc<dyn Window>>, + ) -> bool { self.poll( |x, y| { f(x, y).inspect(|_| { - w.map(|x| x.request_redraw()); + // w.map(|x| x.request_redraw()); }) }, runtime, diff --git a/src/main.rs b/src/main.rs index c48eaca..e70d96a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ #![feature( + adt_const_params, inherent_associated_types, never_type, random, @@ -70,14 +71,17 @@ use lsp_types::*; use rust_fsm::StateMachine; use serde::{Deserialize, Serialize}; use swash::FontRef; -use winit::dpi::PhysicalSize; +use winit::dpi::{PhysicalPosition, PhysicalSize}; use winit::event::{ - ElementState, Event, Ime, MouseButton, MouseScrollDelta, WindowEvent, + ButtonSource, ElementState, Ime, MouseButton, MouseScrollDelta, + WindowEvent, }; use winit::event_loop::{ControlFlow, EventLoop}; +use winit::icon::RgbaIcon; use winit::keyboard::{Key, ModifiersState, NamedKey}; -use winit::platform::wayland::WindowAttributesExtWayland; -use winit::window::Icon; +use winit::window::{ + ImeCapabilities, ImeEnableRequest, ImeHint, ImeRequestData, +}; use crate::edi::Editor; use crate::edi::st::*; @@ -119,7 +123,7 @@ extern "C" fn sigint(_: i32) { } #[implicit_fn::implicit_fn] -pub(crate) fn entry(event_loop: EventLoop<()>) { +pub(crate) fn entry(event_loop: EventLoop) { unsafe { __ED.write(Editor::new()) }; assert_eq!(unsafe { atexit(cleanup) }, 0); unsafe { signal(libc::SIGINT, sigint as *const () as usize) }; @@ -148,16 +152,17 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { move |elwt| { let window = winit_app::make_window(elwt, |x| { x.with_title("gracilaria") + .with_platform_attributes(Box::new(winit::platform::wayland::WindowAttributesWayland::default().with_name("com.bendn.gracilaria", "com.bendn.gracilaria"))) .with_decorations(false) - .with_name("com.bendn.gracilaria", "") - .with_resize_increments(PhysicalSize::new(fw, fh)) + // .with_name("com.bendn.gracilaria", "") + // .with_resize_increments(PhysicalSize::new(fw, fh)) .with_window_icon(Some( - Icon::from_rgba( + RgbaIcon::new( include_bytes!("../dist/icon-32").to_vec(), 32, 32, ) - .unwrap(), + .unwrap().into() )) }); if let Some(x) = w.take() { @@ -165,8 +170,21 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { } let w_ = window.clone(); title.as_deref().map(move |x| w_.set_title(x)); - window.set_ime_allowed(true); - window.set_ime_purpose(winit::window::ImePurpose::Terminal); + window.request_redraw(); + window.request_ime_update(winit::window::ImeRequest::Enable( + ImeEnableRequest::new( + ImeCapabilities::default() + .with_hint_and_purpose() + .with_cursor_area(), + // .with_surrounding_text(), + ImeRequestData::default().with_hint_and_purpose( + ImeHint::NONE, + winit::window::ImePurpose::Terminal, + ).with_cursor_area(winit::dpi::Position::Physical(PhysicalPosition::new(0, 0)), winit::dpi::Size::Physical(PhysicalSize::new(0,0))), + ) + .unwrap(), + )).unwrap(); + // window.set_ime_purpose(winit::window::ImePurpose::Terminal); let context = softbuffer::Context::new(window.clone()).unwrap(); @@ -177,7 +195,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { }, ) .with_event_handler( - move |(window, _context), surface, event, elwt| { + move |(window, _context), surface, window_id, event, elwt| { elwt.set_control_flow(ControlFlow::Wait); let (fw, fh) = dsb::dims(&FONT, ppem); let (c, r) = dsb::fit( @@ -185,8 +203,8 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { ppem, ls, ( - window.inner_size().width as _, - window.inner_size().height as _, + window.surface_size().width as _, + window.surface_size().height as _, ), ); if let t = Editor::modify(ed.origin.as_deref()) @@ -196,14 +214,17 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { ed.state.consume(Action::Changed).unwrap(); window.request_redraw(); } + // let before = ed.state.name(); ed.poll(Some(window)); - + // println!("{before} -> poll -> {}", ed.state.name()); + // let before = ed.state.name(); + // let ev = format!("{event:?}"); + // use WindowEvent as Event; match event { - Event::AboutToWait => {} - Event::WindowEvent { - window_id, - event: WindowEvent::Resized(size), - } if window_id == window.id() => { + // Event::AboutToWait => {} + + + WindowEvent::SurfaceResized(size) if window_id == window.id() => { let Some(surface) = surface else { eprintln!( "Resized fired before Resumed or after \ @@ -232,21 +253,12 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { ] } } - Event::WindowEvent { - event: WindowEvent::Ime(Ime::Preedit(..)), - .. - } => {} - Event::WindowEvent { - event: WindowEvent::Ime(Ime::Commit(x)), - .. - } => { + WindowEvent::Ime(Ime::Preedit(..)) => {} + WindowEvent::Ime(Ime::Commit(x)) => { ed.text.insert(&x); window.request_redraw(); } - Event::WindowEvent { - window_id, - event: WindowEvent::RedrawRequested, - } if window_id == window.id() => { + WindowEvent::RedrawRequested if window_id == window.id() => { rnd::render( ed, &mut cells, @@ -264,16 +276,10 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { ); } - Event::WindowEvent { - event: WindowEvent::CloseRequested, - window_id, - } if window_id == window.id() => { + WindowEvent::CloseRequested => { elwt.exit(); } - Event::WindowEvent { - event: WindowEvent::CursorMoved { position, .. }, - .. - } => { + WindowEvent::PointerMoved { position, .. } => { let met = FONT.metrics(&[]); let fac = ppem / met.units_per_em as f32; cursor_position = ( @@ -283,10 +289,13 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { ); ed.cursor_moved(cursor_position, window.clone(), c); } - Event::WindowEvent { - event: - WindowEvent::MouseInput { state: bt, button, .. }, - .. + + WindowEvent::PointerButton { + state: bt, + button: ButtonSource::Mouse(button), + .. + + } if bt.is_pressed() => { if button == MouseButton::Left { unsafe { CLICKING = true }; @@ -294,42 +303,31 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { ed.click(button, cursor_position, window.clone()); window.request_redraw(); } - Event::WindowEvent { - event: - WindowEvent::MouseInput { - button: MouseButton::Left, + + WindowEvent::PointerButton { + button: ButtonSource::Mouse(MouseButton::Left), .. - }, - .. - } => unsafe { CLICKING = false }, - Event::WindowEvent { - window_id: _, - event: + } + + => unsafe { CLICKING = false }, + WindowEvent::MouseWheel { device_id: _, delta: MouseScrollDelta::LineDelta(_, rows), phase: _, - }, - } => { + } => { ed.scroll(rows); window.request_redraw(); } - Event::WindowEvent { - event: WindowEvent::ModifiersChanged(modifiers), - .. - } => { + WindowEvent::ModifiersChanged(modifiers) => { unsafe { MODIFIERS = modifiers.state() }; window.request_redraw(); } - Event::WindowEvent { - event: - WindowEvent::KeyboardInput { + WindowEvent::KeyboardInput { event, is_synthetic: false, .. - }, - .. - } if event.state == ElementState::Pressed => { + } if event.state == ElementState::Pressed => { // if event.logical_key == Key::Named(NamedKey::F12) { // lsp.unwrap().runtime.spawn(async move { // lsp.unwrap().symbols().await; @@ -337,7 +335,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { // } if matches!( event.logical_key, - Key::Named(Shift | Alt | Control | Super) + Key::Named(Shift | Alt | Control | Meta) ) { return; } @@ -348,6 +346,7 @@ pub(crate) fn entry(event_loop: EventLoop<()>) { } _ => {} }; + // println!("{before} -> {ev} -> {}", ed.state.name()); }, ); winit_app::run_app(event_loop, app); @@ -462,8 +461,9 @@ rust_fsm::state_machine! { None => K(Key<&'i str> => Key::Character(k @ ("." | ":"))) => Complete( RqS<Complete, lsp_types::request::Completion, usize> => default() ) [Request(CompletionContext => CompletionContext {trigger_kind: CompletionTriggerKind::TRIGGER_CHARACTER, trigger_character:Some(k.to_string()) })], - None => K(Key::Named(NamedKey::Space) if ctrl()) => Complete(default()) [Request(CompletionContext { trigger_kind: CompletionTriggerKind::INVOKED, trigger_character:None })], - None => K(Key::Character(x) if x.chars().all(char::is_alphabetic)) => Complete(default()) [Request(CompletionContext { trigger_kind: CompletionTriggerKind::INVOKED, trigger_character:None })], + None => K(Key::Character(" ") if ctrl()) => Complete(default()) [Request(CompletionContext { trigger_kind: CompletionTriggerKind::INVOKED, trigger_character:None })], + None => K(Key::Character(x) if x.chars().all(char::is_alphabetic) + && !matches!(x, "w"|"a"|"s"|"d" if alt())) => Complete(default()) [Request(CompletionContext { trigger_kind: CompletionTriggerKind::INVOKED, trigger_character:None })], None => K(_) => _, // when @@ -14,7 +14,7 @@ use rust_fsm::StateMachine; use softbuffer::Surface; use url::Url; use winit::dpi::{PhysicalPosition, PhysicalSize}; -use winit::window::Window; +use winit::window::{ImeRequestData, Window}; use crate::edi::st::State; use crate::edi::{Editor, lsp_m}; @@ -30,13 +30,13 @@ pub fn render( ed: &mut Editor, cells: &mut [Cell], ppem: f32, - window: &mut Arc<Window>, + window: &mut Arc<dyn Window>, fw: f32, fh: f32, ls: f32, c: usize, r: usize, - surface: Option<&mut Surface<Arc<Window>, Arc<Window>>>, + surface: Option<&mut Surface<Arc<dyn Window>, Arc<dyn Window>>>, cursor_position: (usize, usize), fonts: &mut dsb::Fonts, mut i: Image<&mut [u8], 3>, @@ -45,22 +45,30 @@ pub fn render( let (cx, cy) = text.primary_cursor_visual(); let met = super::FONT.metrics(&[]); let fac = ppem / met.units_per_em as f32; - window.set_ime_cursor_area( - PhysicalPosition::new( - ((cx + text.line_number_offset()) as f64 * (fw) as f64) - .round(), - ((cy.saturating_sub(text.vo)) as f64 * (fh + ls * fac) as f64) - .floor(), - ), - PhysicalSize::new(fw, fh), - ); + window + .request_ime_update(winit::window::ImeRequest::Update( + ImeRequestData::default().with_cursor_area( + PhysicalPosition::new( + ((cx + text.line_number_offset()) as f64 + * (fw) as f64) + .round(), + ((cy.saturating_sub(text.vo)) as f64 + * (fh + ls * fac) as f64) + .floor(), + ) + .into(), + PhysicalSize::new(fw, fh).into(), + ), + )) + .unwrap(); + let Some(surface) = surface else { eprintln!( "RedrawRequested fired before Resumed or after Suspended" ); return; }; - let size = window.inner_size(); + let size = window.surface_size(); if size.height != 0 && size.width != 0 { let now = Instant::now(); @@ -360,8 +368,8 @@ pub fn render( return Err(()); } assert!( - w < window.inner_size().width as _ - && h < window.inner_size().height as _ + w < window.surface_size().width as _ + && h < window.surface_size().height as _ ); let is_above = position.1.checked_sub(h).is_some(); let top = position.1.checked_sub(h).unwrap_or( @@ -373,17 +381,17 @@ pub fn render( ppem, ls, ( - window.inner_size().width as _, /* - left */ - ((window.inner_size().height as usize) + window.surface_size().width as _, /* - left */ + ((window.surface_size().height as usize) .saturating_sub(top)), ), ); /* suspicious saturation */ r = r.min(y); let left = if position.0 + w as usize - > window.inner_size().width as usize + > window.surface_size().width as usize { - window.inner_size().width as usize - w as usize + window.surface_size().width as usize - w as usize } else { position.0 }; @@ -645,33 +653,30 @@ pub fn render( }) }); let mut drawb = |cells, c| { - // let ws = ed.workspace.as_deref().unwrap(); - // let (_x, _y) = text.cursor_visual(); - let _x = 0; - let _y = r - 1; - let Ok((_, left, top, w, h)) = place_around( - (_x, _y), - i.copy(), - cells, - c, - ppem, - ls, - 0., - 0., - 0., - ) else { - println!("ra?"); - return; - }; - i.r#box( - ( - left.saturating_sub(1) as _, - top.saturating_sub(1) as _, - ), - w as _, - h as _, - BORDER, - ); + // let ws = ed.workspace.as_deref().unwrap(); + // let (_x, _y) = text.cursor_visual(); + let _x = 0; + let _y = r - 1; + let Ok((_, left, top, w, h)) = place_around( + (_x, _y), + i.copy(), + cells, + c, + ppem, + ls, + 0., + 0., + 0., + ) else { + println!("ra?"); + return; + }; + i.r#box( + (left.saturating_sub(1) as _, top.saturating_sub(1) as _), + w as _, + h as _, + BORDER, + ); }; match &ed.state { State::CodeAction(Rq { result: Some(x), .. }) => 'out: { @@ -717,7 +722,7 @@ pub fn render( let c = x.cells(50, ws); drawb(&c, 50); } - State::Runnables(Rq { result:Some(x), .. }) => { + State::Runnables(Rq { result: Some(x), .. }) => { let ws = ed.workspace.as_deref().unwrap(); let c = x.cells(50, ws); drawb(&c, 50); diff --git a/src/winit_app.rs b/src/winit_app.rs index 18f7258..adba393 100644 --- a/src/winit_app.rs +++ b/src/winit_app.rs @@ -1,57 +1,72 @@ /// Common boilerplate for setting up a winit application. use std::marker::PhantomData; -use std::sync::Arc; +use std::sync::Arc as Rc; use winit::application::ApplicationHandler; -use winit::event::{Event, WindowEvent}; +use winit::event::{DeviceEvent, DeviceId, WindowEvent}; use winit::event_loop::{ActiveEventLoop, EventLoop}; use winit::window::{Window, WindowAttributes, WindowId}; /// Run a Winit application. #[allow(unused_mut)] pub(crate) fn run_app( - event_loop: EventLoop<()>, - mut app: impl ApplicationHandler<()> + 'static, + event_loop: EventLoop, + mut app: impl ApplicationHandler + 'static, ) { - #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] - event_loop.run_app(&mut app).unwrap(); + #[cfg(not(target_family = "wasm"))] + event_loop.run_app(app).unwrap(); - #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] + #[cfg(target_family = "wasm")] winit::platform::web::EventLoopExtWebSys::spawn_app(event_loop, app); } /// Create a window from a set of window attributes. #[allow(dead_code)] pub(crate) fn make_window( - elwt: &ActiveEventLoop, + elwt: &dyn ActiveEventLoop, f: impl FnOnce(WindowAttributes) -> WindowAttributes, -) -> Arc<Window> { +) -> Rc<dyn Window> { let attributes = f(WindowAttributes::default()); - #[cfg(target_arch = "wasm32")] + #[cfg(target_family = "wasm")] let attributes = winit::platform::web::WindowAttributesExtWebSys::with_append( attributes, true, ); + let window = elwt.create_window(attributes); - Arc::new(window.unwrap()) + window.unwrap().into() } /// Easily constructable winit application. -pub(crate) struct WinitApp<T, S, Init, InitSurface, Handler> { +pub(crate) struct WinitApp< + T, + S, + Init, + InitSurface, + Handler, + DeviceEventHandler, + AboutToWaitHandler, +> { /// Closure to initialize `state`. - pub init: Init, + init: Init, /// Closure to initialize `surface_state`. - pub init_surface: InitSurface, + init_surface: InitSurface, /// Closure to run on window events. - pub event: Handler, + event: Handler, + + /// Closure to run on device events. + device_event: DeviceEventHandler, + + /// Closure to run on about_to_wait events. + about_to_wait: AboutToWaitHandler, /// Contained state. - pub state: Option<T>, + state: Option<T>, /// Contained surface state. - pub surface_state: Option<S>, + surface_state: Option<S>, } /// Builder that makes it so we don't have to name `T`. @@ -68,8 +83,8 @@ pub(crate) struct WinitAppBuilder<T, S, Init, InitSurface> { impl<T, S, Init, InitSurface> WinitAppBuilder<T, S, Init, InitSurface> where - Init: FnMut(&ActiveEventLoop) -> T, - InitSurface: FnMut(&ActiveEventLoop, &mut T) -> S, + Init: FnMut(&dyn ActiveEventLoop) -> T, + InitSurface: FnMut(&dyn ActiveEventLoop, &mut T) -> S, { /// Create with an "init" closure. pub(crate) fn with_init( @@ -80,55 +95,173 @@ where } /// Build a new application. + #[allow(clippy::type_complexity)] pub(crate) fn with_event_handler<F>( self, handler: F, - ) -> WinitApp<T, S, Init, InitSurface, F> + ) -> WinitApp< + T, + S, + Init, + InitSurface, + F, + impl FnMut(&mut T, Option<&mut S>, DeviceEvent, &dyn ActiveEventLoop), + impl FnMut(&mut T, Option<&mut S>, &dyn ActiveEventLoop), + > where - F: FnMut(&mut T, Option<&mut S>, Event<()>, &ActiveEventLoop), + F: FnMut( + &mut T, + Option<&mut S>, + WindowId, + WindowEvent, + &dyn ActiveEventLoop, + ), { - WinitApp::new(self.init, self.init_surface, handler) + WinitApp::new( + self.init, + self.init_surface, + handler, + |_, _, _, _| {}, + |_, _, _| {}, + ) } } -impl<T, S, Init, InitSurface, Handler> - WinitApp<T, S, Init, InitSurface, Handler> +impl< + T, + S, + Init, + InitSurface, + Handler, + DeviceEventHandler, + AboutToWaitHandler, +> + WinitApp< + T, + S, + Init, + InitSurface, + Handler, + DeviceEventHandler, + AboutToWaitHandler, + > where - Init: FnMut(&ActiveEventLoop) -> T, - InitSurface: FnMut(&ActiveEventLoop, &mut T) -> S, - Handler: FnMut(&mut T, Option<&mut S>, Event<()>, &ActiveEventLoop), + Init: FnMut(&dyn ActiveEventLoop) -> T, + InitSurface: FnMut(&dyn ActiveEventLoop, &mut T) -> S, + Handler: FnMut( + &mut T, + Option<&mut S>, + WindowId, + WindowEvent, + &dyn ActiveEventLoop, + ), + DeviceEventHandler: + FnMut(&mut T, Option<&mut S>, DeviceEvent, &dyn ActiveEventLoop), + AboutToWaitHandler: + FnMut(&mut T, Option<&mut S>, &dyn ActiveEventLoop), { /// Create a new application. pub(crate) fn new( init: Init, init_surface: InitSurface, event: Handler, + device_event: DeviceEventHandler, + about_to_wait: AboutToWaitHandler, ) -> Self { Self { init, init_surface, event, + device_event, + about_to_wait, state: None, surface_state: None, } } + + /// Build a new application. + #[allow(dead_code)] + pub(crate) fn with_about_to_wait_handler<F>( + self, + about_to_wait: F, + ) -> WinitApp<T, S, Init, InitSurface, Handler, DeviceEventHandler, F> + where + F: FnMut(&mut T, Option<&mut S>, &dyn ActiveEventLoop), + { + WinitApp::new( + self.init, + self.init_surface, + self.event, + self.device_event, + about_to_wait, + ) + } + + /// Build a new application. + #[allow(dead_code)] + pub(crate) fn with_device_event_handler<F>( + self, + device_event: F, + ) -> WinitApp<T, S, Init, InitSurface, Handler, F, AboutToWaitHandler> + where + F: FnMut( + &mut T, + Option<&mut S>, + DeviceEvent, + &dyn ActiveEventLoop, + ), + { + WinitApp::new( + self.init, + self.init_surface, + self.event, + device_event, + self.about_to_wait, + ) + } } -impl<T, S, Init, InitSurface, Handler> ApplicationHandler - for WinitApp<T, S, Init, InitSurface, Handler> +impl< + T, + S, + Init, + InitSurface, + Handler, + DeviceEventHandler, + AboutToWaitHandler, +> ApplicationHandler + for WinitApp< + T, + S, + Init, + InitSurface, + Handler, + DeviceEventHandler, + AboutToWaitHandler, + > where - Init: FnMut(&ActiveEventLoop) -> T, - InitSurface: FnMut(&ActiveEventLoop, &mut T) -> S, - Handler: FnMut(&mut T, Option<&mut S>, Event<()>, &ActiveEventLoop), + Init: FnMut(&dyn ActiveEventLoop) -> T, + InitSurface: FnMut(&dyn ActiveEventLoop, &mut T) -> S, + Handler: FnMut( + &mut T, + Option<&mut S>, + WindowId, + WindowEvent, + &dyn ActiveEventLoop, + ), + DeviceEventHandler: + FnMut(&mut T, Option<&mut S>, DeviceEvent, &dyn ActiveEventLoop), + AboutToWaitHandler: + FnMut(&mut T, Option<&mut S>, &dyn ActiveEventLoop), { - fn resumed(&mut self, el: &ActiveEventLoop) { + fn resumed(&mut self, el: &dyn ActiveEventLoop) { debug_assert!(self.state.is_none()); let mut state = (self.init)(el); self.surface_state = Some((self.init_surface)(el, &mut state)); self.state = Some(state); } - fn suspended(&mut self, _event_loop: &ActiveEventLoop) { + fn suspended(&mut self, _event_loop: &dyn ActiveEventLoop) { let surface_state = self.surface_state.take(); debug_assert!(surface_state.is_some()); drop(surface_state); @@ -136,28 +269,36 @@ where fn window_event( &mut self, - event_loop: &ActiveEventLoop, + event_loop: &dyn ActiveEventLoop, window_id: WindowId, event: WindowEvent, ) { let state = self.state.as_mut().unwrap(); let surface_state = self.surface_state.as_mut(); - (self.event)( - state, - surface_state, - Event::WindowEvent { window_id, event }, - event_loop, - ); + (self.event)(state, surface_state, window_id, event, event_loop); } - fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) { + fn about_to_wait(&mut self, event_loop: &dyn ActiveEventLoop) { if let Some(state) = self.state.as_mut() { - (self.event)( - state, - self.surface_state.as_mut(), - Event::AboutToWait, - event_loop, - ); + let surface_state = self.surface_state.as_mut(); + (self.about_to_wait)(state, surface_state, event_loop); } } + + fn device_event( + &mut self, + event_loop: &dyn ActiveEventLoop, + _device_id: Option<DeviceId>, + event: DeviceEvent, + ) { + let state = self.state.as_mut().unwrap(); + let surface_state = self.surface_state.as_mut(); + (self.device_event)(state, surface_state, event, event_loop); + } + + fn can_create_surfaces(&mut self, el: &dyn ActiveEventLoop) { + let mut state = (self.init)(el); + self.surface_state = Some((self.init_surface)(el, &mut state)); + self.state = Some(state); + } } |