diff --git a/src/bridge/events.rs b/src/bridge/events.rs index 3527e78..2067555 100644 --- a/src/bridge/events.rs +++ b/src/bridge/events.rs @@ -5,6 +5,8 @@ use std::convert::TryInto; use rmpv::Value; use skulpin::skia_safe::Color4f; +use crate::editor::EDITOR; +use crate::error_handling::ResultPanicExplanation; use crate::editor::{Colors, Style, CursorMode, CursorShape}; #[derive(Debug, Clone)] @@ -160,8 +162,7 @@ fn unpack_color(packed_color: u64) -> Color4f { } } -fn extract_values>(values: Vec, mut arr: Arr) -> Result -{ +fn extract_values>(values: Vec, mut arr: Arr) -> Result { let arr_ref = arr.as_mut(); if values.len() != arr_ref.len() { @@ -649,17 +650,16 @@ pub fn parse_redraw_event(event_value: Value) -> Result> { Ok(parsed_events) } -pub(in super) fn parse_neovim_event(event_name: &str, arguments: Vec) -> Result> { - let mut resulting_events = Vec::new(); - if event_name == "redraw" { - resulting_events.reserve(arguments.len()); - for event in arguments { - resulting_events.append(&mut parse_redraw_event(event)?); +pub(in super) fn handle_redraw_event_group(arguments: Vec) { + for events in arguments { + let parsed_events = parse_redraw_event(events) + .unwrap_or_explained_panic("Could not parse event from neovim"); + + for parsed_event in parsed_events { + let mut editor = EDITOR.lock(); + editor.handle_redraw_event(parsed_event); } - } else { - println!("Unknown global event {}", event_name); } - Ok(resulting_events) } diff --git a/src/bridge/handler.rs b/src/bridge/handler.rs index a80a5f1..ac604e9 100644 --- a/src/bridge/handler.rs +++ b/src/bridge/handler.rs @@ -2,11 +2,11 @@ use rmpv::Value; use nvim_rs::{Neovim, Handler, compat::tokio::Compat}; use async_trait::async_trait; use tokio::process::ChildStdin; +use tokio::task; use log::trace; -use crate::error_handling::ResultPanicExplanation; -use crate::editor::EDITOR; -use super::events::parse_neovim_event; +use crate::settings::SETTINGS; +use super::events::handle_redraw_event_group; #[derive(Clone)] pub struct NeovimHandler(); @@ -17,11 +17,16 @@ impl Handler for NeovimHandler { async fn handle_notify(&self, event_name: String, arguments: Vec, _neovim: Neovim>) { trace!("Neovim notification: {:?}", &event_name); - let parsed_events = parse_neovim_event(&event_name, arguments) - .unwrap_or_explained_panic("Could not parse event from neovim"); - for event in parsed_events { - let mut editor = EDITOR.lock(); - editor.handle_redraw_event(event); - } + task::spawn_blocking(move || { + match event_name.as_ref() { + "redraw" => { + handle_redraw_event_group(arguments); + }, + "setting_changed" => { + SETTINGS.handle_changed_notification(arguments); + }, + _ => {} + } + }).await.ok(); } } diff --git a/src/bridge/mod.rs b/src/bridge/mod.rs index b836d21..51d4c73 100644 --- a/src/bridge/mod.rs +++ b/src/bridge/mod.rs @@ -95,7 +95,6 @@ async fn start_process(mut receiver: UnboundedReceiver) { info!("Neovim process attached"); let nvim = Arc::new(nvim); - let input_nvim = nvim.clone(); tokio::spawn(async move { info!("UiCommand processor started"); @@ -117,6 +116,9 @@ async fn start_process(mut receiver: UnboundedReceiver) { } }); + SETTINGS.read_initial_values(&nvim).await; + SETTINGS.setup_changed_listeners(&nvim).await; + nvim.set_option("lazyredraw", Value::Boolean(false)).await .ok(); } diff --git a/src/redraw_scheduler.rs b/src/redraw_scheduler.rs index 8ca30e7..a6545dc 100644 --- a/src/redraw_scheduler.rs +++ b/src/redraw_scheduler.rs @@ -17,7 +17,6 @@ pub struct RedrawScheduler { impl RedrawScheduler { pub fn new() -> RedrawScheduler { - RedrawScheduler { frames_queued: AtomicU16::new(1), scheduled_frame: Mutex::new(None) @@ -38,7 +37,7 @@ impl RedrawScheduler { pub fn queue_next_frame(&self) { trace!("Next frame queued"); - let buffer_frames = SETTINGS.buffer_frames.load(Ordering::Relaxed); + let buffer_frames = SETTINGS.get("extra_buffer_frames").read_u16(); self.frames_queued.store(buffer_frames, Ordering::Relaxed); } diff --git a/src/settings.rs b/src/settings.rs index 57555f8..19de78d 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,19 +1,129 @@ -use std::sync::atomic::{AtomicBool, AtomicU16}; +use std::collections::HashMap; +use std::sync::atomic::{AtomicBool, AtomicU16, Ordering}; +use std::convert::TryInto; +use rmpv::Value; +use nvim_rs::Neovim; +use nvim_rs::compat::tokio::Compat; use flexi_logger::{Logger, Criterion, Naming, Cleanup}; +use tokio::process::ChildStdin; + +use crate::error_handling::ResultPanicExplanation; lazy_static! { pub static ref SETTINGS: Settings = Settings::new(); } +pub enum Setting { + Bool(AtomicBool), + U16(AtomicU16) +} + +impl Setting { + fn new_bool(value: bool) -> Setting { + Setting::Bool(AtomicBool::new(value)) + } + + pub fn read_bool(&self) -> bool { + if let Setting::Bool(atomic_bool) = self { + atomic_bool.load(Ordering::Relaxed) + } else { + panic!("Could not read setting as bool"); + } + } + + fn new_u16(value: u16) -> Setting { + Setting::U16(AtomicU16::new(value)) + } + + pub fn read_u16(&self) -> u16 { + if let Setting::U16(atomic_u16) = self { + atomic_u16.load(Ordering::Relaxed) + } else { + panic!("Could not read setting as u16"); + } + } + + fn parse(&self, value: Value) { + match self { + Setting::Bool(atomic_bool) => { + if let Ok(value) = value.try_into() { + let intermediate: u64 = value; + atomic_bool.store(intermediate != 0, Ordering::Relaxed); + } + }, + Setting::U16(atomic_u16) => { + if let Ok(value) = value.try_into() { + let intermediate: u64 = value; + atomic_u16.store(intermediate as u16, Ordering::Relaxed); + } + } + } + } + + fn unparse(&self) -> Value { + match self { + Setting::Bool(atomic_bool) => { + let value = if atomic_bool.load(Ordering::Relaxed) { + 1 + } else { + 0 + }; + Value::from(value) + }, + Setting::U16(atomic_u16) => Value::from(atomic_u16.load(Ordering::Relaxed)) + } + } +} + pub struct Settings { pub neovim_arguments: Vec, - - pub no_idle: AtomicBool, - pub buffer_frames: AtomicU16 + pub settings: HashMap } impl Settings { + pub async fn read_initial_values(&self, nvim: &Neovim>) { + for (name, setting) in self.settings.iter() { + let variable_name = format!("g:neovide_{}", name.to_string()); + if let Ok(value) = nvim.get_var(&variable_name).await { + setting.parse(value); + } else { + nvim.set_var(&variable_name, setting.unparse()).await.ok(); + } + } + } + + pub async fn setup_changed_listeners(&self, nvim: &Neovim>) { + for name in self.settings.keys() { + let name = name.to_string(); + let vimscript = + format!("function NeovideNotify{}Changed(d, k, z)\n", name) + + &format!(" call rpcnotify(1, \"setting_changed\", \"{}\", g:neovide_{})\n", name, name) + + "endfunction\n" + + &format!("call dictwatcheradd(g:, \"neovide_{}\", \"NeovideNotify{}Changed\")", name, name); + nvim.exec(&vimscript, false).await + .unwrap_or_explained_panic(&format!("Could not setup setting notifier for {}", name)); + } + } + + pub fn handle_changed_notification(&self, arguments: Vec) { + let mut arguments = arguments.into_iter(); + let (name, value) = (arguments.next().unwrap(), arguments.next().unwrap()); + dbg!(&name, &value); + + if let Some(setting) = name + .try_into() + .ok() + .as_ref() + .and_then(|name: &String| self.settings.get(name)) { + setting.parse(value); + } + } + + pub fn get(&self, name: &str) -> &Setting { + self.settings.get(name).expect(&format!("Could not find option {}", name)) + } + pub fn new() -> Settings { let mut no_idle = false; let mut buffer_frames = 1; @@ -37,10 +147,11 @@ impl Settings { } }).collect::>(); - Settings { - neovim_arguments, - no_idle: AtomicBool::new(no_idle), - buffer_frames: AtomicU16::new(buffer_frames), - } + let mut settings = HashMap::new(); + + settings.insert("no_idle".to_string(), Setting::new_bool(no_idle)); + settings.insert("extra_buffer_frames".to_string(), Setting::new_u16(buffer_frames)); + + Settings { neovim_arguments, settings } } } diff --git a/src/window.rs b/src/window.rs index ac30734..c362202 100644 --- a/src/window.rs +++ b/src/window.rs @@ -214,7 +214,7 @@ pub fn ui_loop() { window.set_title(&title); } - if REDRAW_SCHEDULER.should_draw() || SETTINGS.no_idle.load(Ordering::Relaxed) { + if REDRAW_SCHEDULER.should_draw() || SETTINGS.get("no_idle").read_bool() { debug!("Render Triggered"); if skulpin_renderer.draw(&window, |canvas, coordinate_system_helper| { if renderer.draw(canvas, coordinate_system_helper) {