diff --git a/Cargo.lock b/Cargo.lock index 269ccfa2c..7903b158f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2357,7 +2357,7 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "steel-core" version = "0.6.0" -source = "git+https://github.com/mattwparas/steel.git#c10339bcda9808c326d5aebda610d920fc436a1f" +source = "git+https://github.com/mattwparas/steel.git#c5451eaf7fc8e4ba39eb0536b0ccc9b469c2e50b" dependencies = [ "abi_stable", "anyhow", @@ -2395,7 +2395,7 @@ dependencies = [ [[package]] name = "steel-derive" version = "0.5.0" -source = "git+https://github.com/mattwparas/steel.git#c10339bcda9808c326d5aebda610d920fc436a1f" +source = "git+https://github.com/mattwparas/steel.git#c5451eaf7fc8e4ba39eb0536b0ccc9b469c2e50b" dependencies = [ "proc-macro2", "quote", @@ -2405,7 +2405,7 @@ dependencies = [ [[package]] name = "steel-gen" version = "0.2.0" -source = "git+https://github.com/mattwparas/steel.git#c10339bcda9808c326d5aebda610d920fc436a1f" +source = "git+https://github.com/mattwparas/steel.git#c5451eaf7fc8e4ba39eb0536b0ccc9b469c2e50b" dependencies = [ "codegen", "serde", @@ -2415,7 +2415,7 @@ dependencies = [ [[package]] name = "steel-parser" version = "0.6.0" -source = "git+https://github.com/mattwparas/steel.git#c10339bcda9808c326d5aebda610d920fc436a1f" +source = "git+https://github.com/mattwparas/steel.git#c5451eaf7fc8e4ba39eb0536b0ccc9b469c2e50b" dependencies = [ "fxhash", "lasso", diff --git a/helix-term/src/commands/engine/components.rs b/helix-term/src/commands/engine/components.rs index f2519224f..5861377e7 100644 --- a/helix-term/src/commands/engine/components.rs +++ b/helix-term/src/commands/engine/components.rs @@ -26,7 +26,6 @@ use crate::{ Context, }, compositor::{self, Component}, - ctrl, key, ui::overlay::overlaid, }; @@ -40,8 +39,6 @@ struct AsyncReader { } impl AsyncReader { - // TODO: Add &mut references to these async functions - // to avoid the cloning, and to ditch the arc and mutex async fn read_line(self) -> Option { let mut buf = String::new(); @@ -53,19 +50,34 @@ impl AsyncReader { let fut = guard.recv(); - match tokio::time::timeout(std::time::Duration::from_millis(2), fut).await { - Ok(Some(v)) => { - buf.push_str(&v); - Some(buf) + // If we haven't found any characters, just wait until we have something. + // Otherwise, we give this a 2 ms buffer to check if more things are + // coming through the pipe. + if buf.is_empty() { + let next = fut.await; + + match next { + Some(v) => { + buf.push_str(&v); + Some(buf) + } + None => None, } - Ok(None) => { - if buf.is_empty() { - None - } else { + } else { + match tokio::time::timeout(std::time::Duration::from_millis(2), fut).await { + Ok(Some(v)) => { + buf.push_str(&v); Some(buf) } + Ok(None) => { + if buf.is_empty() { + None + } else { + Some(buf) + } + } + Err(_) => Some(buf), } - Err(_) => Some(buf), } } } @@ -254,6 +266,12 @@ pub fn helix_component_module() -> BuiltInModule { "event-result/consume", SteelEventResult::Consumed.into_steelval().unwrap(), ) + .register_value( + "event-result/consume-without-rerender", + SteelEventResult::ConsumedWithoutRerender + .into_steelval() + .unwrap(), + ) .register_value( "event-result/ignore", SteelEventResult::Ignored.into_steelval().unwrap(), @@ -439,7 +457,7 @@ pub struct SteelDynamicComponent { // TODO: currently the component id requires using a &'static str, // however in a world with dynamic components that might not be // the case anymore - _name: String, + name: String, // This _should_ be a struct, but in theory can be whatever you want. It will be the first argument // passed to the functions in the remainder of the struct. state: SteelVal, @@ -463,7 +481,7 @@ impl SteelDynamicComponent { h: HashMap, ) -> Self { Self { - _name: name, + name, state, render, handle_event: h.get("handle_event").cloned(), @@ -522,11 +540,16 @@ enum SteelEventResult { Consumed, Ignored, Close, + ConsumedWithoutRerender, } impl Custom for SteelEventResult {} impl Component for SteelDynamicComponent { + fn name(&self) -> Option<&str> { + Some(&self.name) + } + fn render( &mut self, area: helix_view::graphics::Rect, @@ -646,6 +669,9 @@ impl Component for SteelDynamicComponent { match value { Ok(SteelEventResult::Close) => close_fn, Ok(SteelEventResult::Consumed) => compositor::EventResult::Consumed(None), + Ok(SteelEventResult::ConsumedWithoutRerender) => { + compositor::EventResult::ConsumedWithoutRerender + } Ok(SteelEventResult::Ignored) => compositor::EventResult::Ignored(None), _ => match event { // ctrl!('c') | key!(Esc) => close_fn, @@ -715,7 +741,7 @@ impl Component for SteelDynamicComponent { Err(_e) => { log::info!("Error: {:?}", _e); (None, CursorKind::Block) - }, + } } } else { (None, helix_view::graphics::CursorKind::Hidden) diff --git a/helix-term/src/commands/engine/steel.rs b/helix-term/src/commands/engine/steel.rs index c5cce26fb..eb82397fa 100644 --- a/helix-term/src/commands/engine/steel.rs +++ b/helix-term/src/commands/engine/steel.rs @@ -1527,7 +1527,11 @@ impl Component for BoxDynComponent { } fn id(&self) -> Option<&'static str> { - None + Some(self.inner.type_name()) + } + + fn name(&self) -> Option<&str> { + self.inner.name() } fn render( @@ -1768,8 +1772,10 @@ fn load_misc_api(engine: &mut Engine, generate_sources: bool) { // Arity 1 module.register_fn("hx.custom-insert-newline", custom_insert_newline); module.register_fn("push-component!", push_component); + module.register_fn("pop-last-component!", pop_last_component_by_name); module.register_fn("enqueue-thread-local-callback", enqueue_command); + template_function_arity_1("pop-last-component!"); template_function_arity_1("hx.custom-insert-newline"); template_function_arity_1("push-component!"); template_function_arity_1("enqueue-thread-local-callback"); @@ -2286,10 +2292,12 @@ fn push_component(cx: &mut Context, component: &mut WrappedDynComponent) { cx.jobs.local_callback(callback); } -fn render(cx: &mut Context) { +fn pop_last_component_by_name(cx: &mut Context, name: SteelString) { let callback = async move { let call: Box = Box::new( - move |_editor: &mut Editor, _compositor: &mut Compositor, _jobs: &mut job::Jobs| {}, + move |_editor: &mut Editor, compositor: &mut Compositor, _jobs: &mut job::Jobs| { + compositor.remove_by_dynamic_name(&name); + }, ); Ok(call) }; diff --git a/helix-term/src/compositor.rs b/helix-term/src/compositor.rs index 96a2cf42d..fbaffc5c2 100644 --- a/helix-term/src/compositor.rs +++ b/helix-term/src/compositor.rs @@ -13,6 +13,7 @@ pub type SyncCallback = Box; pub enum EventResult { Ignored(Option), Consumed(Option), + ConsumedWithoutRerender, } use crate::job::Jobs; @@ -73,6 +74,10 @@ pub trait Component: Any + AnyComponent { fn id(&self) -> Option<&'static str> { None } + + fn name(&self) -> Option<&str> { + None + } } pub struct Compositor { @@ -136,6 +141,14 @@ impl Compositor { Some(self.layers.remove(idx)) } + pub fn remove_by_dynamic_name(&mut self, id: &str) -> Option> { + let idx = self + .layers + .iter() + .position(|layer| layer.name() == Some(id))?; + Some(self.layers.remove(idx)) + } + pub fn handle_event(&mut self, event: &Event, cx: &mut Context) -> bool { // If it is a key event and a macro is being recorded, push the key event to the recording. if let (Event::Key(key), Some((_, keys))) = (event, &mut cx.editor.macro_recording) { @@ -159,6 +172,10 @@ impl Compositor { consumed = true; break; } + // Swallow the event, but don't trigger a re-render + EventResult::ConsumedWithoutRerender => { + break; + } EventResult::Ignored(Some(callback)) => { callbacks.push(callback); }