From e296e2a4ced604ed4ca7b9dc20a0a4ff02fb9154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20Vald=C3=A9s?= Date: Mon, 24 Feb 2020 05:14:06 +0100 Subject: [PATCH 1/6] WIP for structured settings system --- src/main.rs | 3 + src/redraw_scheduler.rs | 35 ++++++++- src/settings.rs | 157 +++++++++++++++------------------------- src/window.rs | 45 +++++++++++- 4 files changed, 137 insertions(+), 103 deletions(-) diff --git a/src/main.rs b/src/main.rs index 80e1332..c020471 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,6 +20,9 @@ use window::ui_loop; pub const INITIAL_DIMENSIONS: (u64, u64) = (100, 50); fn main() { + window::initialize_settings(); + redraw_scheduler::initialize_settings(); + initialize(&BRIDGE); ui_loop(); } diff --git a/src/redraw_scheduler.rs b/src/redraw_scheduler.rs index a6545dc..57f13e7 100644 --- a/src/redraw_scheduler.rs +++ b/src/redraw_scheduler.rs @@ -4,12 +4,41 @@ use std::time::Instant; use log::trace; -use crate::settings::SETTINGS; +use crate::settings::{SETTINGS, Value}; lazy_static! { pub static ref REDRAW_SCHEDULER: RedrawScheduler = RedrawScheduler::new(); } +#[derive(Clone)] +struct RedrawSettings { + extra_buffer_frames: u64, +} + +fn parse_changed_setting(name: &str, value: Option) -> Value { + match name { + "extra_buffer_frames" => { + let mut settings = SETTINGS.get::(); + if let Some(value) = value { + settings.extra_buffer_frames= value.as_u64().unwrap(); // TODO -- handle wrong data type + SETTINGS.set(&settings); + } + Value::from(settings.extra_buffer_frames) + } + _ => { + panic!(format!("Unknown setting: {}", name)); + } + } +} + +pub fn initialize_settings() { + SETTINGS.set(&RedrawSettings { extra_buffer_frames: 60 }); + + SETTINGS.add_listener("extra_buffer_frames", parse_changed_setting); +} + + + pub struct RedrawScheduler { frames_queued: AtomicU16, scheduled_frame: Mutex> @@ -37,8 +66,8 @@ impl RedrawScheduler { pub fn queue_next_frame(&self) { trace!("Next frame queued"); - let buffer_frames = SETTINGS.get("extra_buffer_frames").read_u16(); - self.frames_queued.store(buffer_frames, Ordering::Relaxed); + let buffer_frames = SETTINGS.get::().extra_buffer_frames; + self.frames_queued.store(buffer_frames as u16, Ordering::Relaxed); } pub fn should_draw(&self) -> bool { diff --git a/src/settings.rs b/src/settings.rs index 08a6bdb..f09c113 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,12 +1,13 @@ use std::collections::HashMap; use std::convert::TryInto; +use std::any::{Any, TypeId}; -use rmpv::Value; +pub 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 parking_lot::Mutex; +use parking_lot::RwLock; use log::warn; use crate::error_handling::ResultPanicExplanation; @@ -15,120 +16,86 @@ lazy_static! { pub static ref SETTINGS: Settings = Settings::new(); } -#[derive(Debug)] -pub enum Setting { - Bool(bool), - U16(u16), - String(String) +struct SettingsObject { + object: Box, } -impl Setting { - fn new_bool(value: bool) -> Setting { - Setting::Bool(value) - } - - pub fn read_bool(&self) -> bool { - if let Setting::Bool(value) = self { - *value - } else { - panic!("Could not read setting as bool"); - } - } +pub struct Settings { + pub neovim_arguments: Vec, + settings: RwLock>, + listeners: RwLock)->Value>>, +} - fn new_u16(value: u16) -> Setting { - Setting::U16(value) - } +impl Settings { - pub fn read_u16(&self) -> u16 { - if let Setting::U16(value) = self { - *value - } else { - panic!("Could not read setting as u16"); - } - } - - fn new_string(value: String) -> Setting { - Setting::String(value) - } + fn new() -> Settings { - pub fn read_string(&self) -> String { - if let Setting::String(value) = self { - value.clone() - } else { - panic!("Could not read setting as string"); - } - } + let mut no_idle = false; + let mut buffer_frames = 1; - fn parse(&mut self, value: Value) { - match self { - Setting::Bool(internal_bool) => { - if let Ok(value) = value.try_into() { - let intermediate: u64 = value; - *internal_bool = intermediate != 0; - } - }, - Setting::U16(internal_u16) => { - if let Ok(value) = value.try_into() { - let intermediate: u64 = value; - *internal_u16 = intermediate as u16; - } - }, - Setting::String(internal_string) => { - if let Ok(value) = value.try_into() { - let intermediate: String = value; - *internal_string = intermediate; - } + let neovim_arguments = std::env::args().filter(|arg| { + if arg == "--log" { + Logger::with_str("neovide") + .log_to_file() + .rotate(Criterion::Size(10_000_000), Naming::Timestamps, Cleanup::KeepLogFiles(1)) + .start() + .expect("Could not start logger"); + false + } else if arg == "--noIdle" { + no_idle = true; + false + } else if arg == "--extraBufferFrames" { + buffer_frames = 60; + false + } else { + true } - } - } + }).collect::>(); - fn unparse(&self) -> Value { - match self { - Setting::Bool(internal_bool) => { - let value = if *internal_bool { - 1 - } else { - 0 - }; - Value::from(value) - }, - Setting::U16(internal_u16) => Value::from(*internal_u16), - Setting::String(internal_string) => Value::from(internal_string.as_str()), + + Settings{ + neovim_arguments, + settings: RwLock::new(HashMap::new()), + listeners: RwLock::new(HashMap::new()), } } - fn clone(&self) -> Setting { - match self { - Setting::Bool(_) => Setting::new_bool(self.read_bool()), - Setting::U16(_) => Setting::new_u16(self.read_u16()), - Setting::String(_) => Setting::new_string(self.read_string()), - } + pub fn add_listener(&self, property_name: &str, func: fn (&str, Option)-> Value) { + self.listeners.write().insert(String::from(property_name), func); + } + + pub fn set(&self, t: &T) { + let type_id : TypeId = TypeId::of::(); + let t : T = (*t).clone(); + self.settings.write().insert(type_id, SettingsObject{ object: Box::new(t)}); } -} -pub struct Settings { - pub neovim_arguments: Vec, - pub settings: Mutex> -} + pub fn get<'a, T: Clone + Send + Sync + 'static>(&'a self) -> T { + let read_lock = self.settings.read(); + let boxed = &read_lock.get(&TypeId::of::()).expect("Trying to retrieve a settings object that doesn't exist"); + let value: &T = boxed.object.downcast_ref::().expect("Attempted to extract a settings object of the wrong type"); + (*value).clone() + } -impl Settings { pub async fn read_initial_values(&self, nvim: &Neovim>) { - let keys : Vec = self.settings.lock().keys().cloned().collect(); + let keys : Vec = self.listeners.read().keys().cloned().collect(); for name in keys { let variable_name = format!("neovide_{}", name.to_string()); match nvim.get_var(&variable_name).await { - Ok(value) => self.settings.lock().get_mut(&name).unwrap().parse(value), + Ok(value) => { + self.listeners.read().get(&name).unwrap()(&name, Some(value)); + }, Err(error) => { warn!("Initial value load failed for {}: {}", name, error); - let setting = self.get(&name); - nvim.set_var(&variable_name, setting.unparse()).await.ok(); + let setting = self.listeners.read().get(&name).unwrap()(&name, None); + nvim.set_var(&variable_name, setting).await.ok(); } } } } pub async fn setup_changed_listeners(&self, nvim: &Neovim>) { - let keys : Vec = self.settings.lock().keys().cloned().collect(); + let keys : Vec = self.listeners.read().keys().cloned().collect(); for name in keys { let vimscript = format!( concat!( @@ -151,15 +118,10 @@ impl Settings { let name: Result= name.try_into(); let name = name.unwrap(); - self.settings.lock().get_mut(&name).unwrap().parse(value); - } - - pub fn get(&self, name: &str) -> Setting { - let settings = self.settings.lock(); - let setting = settings.get(name).expect(&format!("Could not find option {}", name)); - setting.clone() + self.listeners.read().get(&name).unwrap()(&name, Some(value)); } + /* pub fn new() -> Settings { let mut no_idle = false; let mut buffer_frames = 1; @@ -191,4 +153,5 @@ impl Settings { Settings { neovim_arguments, settings: Mutex::new(settings) } } + */ } diff --git a/src/window.rs b/src/window.rs index da5e14b..bbdc269 100644 --- a/src/window.rs +++ b/src/window.rs @@ -14,7 +14,7 @@ use crate::bridge::{parse_keycode, append_modifiers, BRIDGE, UiCommand}; use crate::renderer::Renderer; use crate::redraw_scheduler::REDRAW_SCHEDULER; use crate::editor::EDITOR; -use crate::settings::SETTINGS; +use crate::settings::{SETTINGS, Value}; use crate::INITIAL_DIMENSIONS; #[derive(RustEmbed)] @@ -236,7 +236,7 @@ impl WindowWrapper { } debug!("Render Triggered"); - if REDRAW_SCHEDULER.should_draw() || SETTINGS.get("no_idle").read_bool() { + if REDRAW_SCHEDULER.should_draw() || SETTINGS.get::().no_idle { let renderer = &mut self.renderer; if self.skulpin_renderer.draw(&self.window, |canvas, coordinate_system_helper| { if renderer.draw(canvas, coordinate_system_helper) { @@ -251,6 +251,45 @@ impl WindowWrapper { } } +#[derive(Clone)] +struct WindowSettings { + refresh_rate: u64, + no_idle: bool, +} + +fn parse_changed_setting(name: &str, value: Option) -> Value { + let mut settings = SETTINGS.get::(); + match name { + "refresh_rate" => { + if let Some(value) = value { + settings.refresh_rate = value.as_u64().unwrap(); // TODO -- handle wrong data type + SETTINGS.set(&settings); + } + Value::from(settings.refresh_rate) + }, + "no_idle" => { + if let Some(value) = value { + settings.no_idle = value.as_bool().unwrap(); // TODO -- handle wrong data type + SETTINGS.set(&settings); + } + Value::from(settings.no_idle) + }, + _ => { + panic!(format!("Unknown setting: {}", name)); + } + } +} + +pub fn initialize_settings() { + SETTINGS.set(&WindowSettings { + refresh_rate: 60, + no_idle: false, + }); + + SETTINGS.add_listener("refresh_rate", parse_changed_setting); + SETTINGS.add_listener("no_idle", parse_changed_setting); +} + pub fn ui_loop() { let mut window = WindowWrapper::new(); @@ -280,7 +319,7 @@ pub fn ui_loop() { } let elapsed = frame_start.elapsed(); - let refresh_rate = SETTINGS.get("refresh_rate").read_u16() as f32; + let refresh_rate = SETTINGS.get::().refresh_rate as f32; let frame_length = Duration::from_secs_f32(1.0 / refresh_rate); if elapsed < frame_length { sleep(frame_length - elapsed); From 40dd9404ec6efe1d698639ccbb0bc408b71d388b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20Vald=C3=A9s?= Date: Mon, 24 Feb 2020 05:21:03 +0100 Subject: [PATCH 2/6] Code formatting --- src/redraw_scheduler.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/redraw_scheduler.rs b/src/redraw_scheduler.rs index 57f13e7..5665398 100644 --- a/src/redraw_scheduler.rs +++ b/src/redraw_scheduler.rs @@ -20,7 +20,7 @@ fn parse_changed_setting(name: &str, value: Option) -> Value { "extra_buffer_frames" => { let mut settings = SETTINGS.get::(); if let Some(value) = value { - settings.extra_buffer_frames= value.as_u64().unwrap(); // TODO -- handle wrong data type + settings.extra_buffer_frames = value.as_u64().unwrap(); // TODO -- handle wrong data type SETTINGS.set(&settings); } Value::from(settings.extra_buffer_frames) @@ -32,13 +32,13 @@ fn parse_changed_setting(name: &str, value: Option) -> Value { } pub fn initialize_settings() { - SETTINGS.set(&RedrawSettings { extra_buffer_frames: 60 }); + SETTINGS.set(&RedrawSettings { + extra_buffer_frames: 60, + }); SETTINGS.add_listener("extra_buffer_frames", parse_changed_setting); } - - pub struct RedrawScheduler { frames_queued: AtomicU16, scheduled_frame: Mutex> From 0a726c2f776838f67f92bf955e72ff26826ef3c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20Vald=C3=A9s?= Date: Tue, 25 Feb 2020 05:02:49 +0100 Subject: [PATCH 3/6] Different, cleaner implementation of structured settings --- src/bridge/mod.rs | 2 +- src/main.rs | 5 +- src/redraw_scheduler.rs | 21 +------ src/settings.rs | 130 +++++++++++++++++++++++++++------------- src/window.rs | 31 ++-------- 5 files changed, 102 insertions(+), 87 deletions(-) diff --git a/src/bridge/mod.rs b/src/bridge/mod.rs index 1f7b755..df5a2ad 100644 --- a/src/bridge/mod.rs +++ b/src/bridge/mod.rs @@ -15,10 +15,10 @@ use log::{info, error, trace}; pub use events::*; pub use keybindings::*; +use crate::settings::*; pub use ui_commands::UiCommand; use handler::NeovimHandler; use crate::error_handling::ResultPanicExplanation; -use crate::settings::SETTINGS; use crate::INITIAL_DIMENSIONS; diff --git a/src/main.rs b/src/main.rs index c020471..bcfa176 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,15 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] +#[macro_use] +mod settings; + mod bridge; mod editor; mod window; mod renderer; mod error_handling; mod redraw_scheduler; -mod settings; + #[macro_use] extern crate derive_new; #[macro_use] extern crate rust_embed; diff --git a/src/redraw_scheduler.rs b/src/redraw_scheduler.rs index 5665398..9780542 100644 --- a/src/redraw_scheduler.rs +++ b/src/redraw_scheduler.rs @@ -4,7 +4,7 @@ use std::time::Instant; use log::trace; -use crate::settings::{SETTINGS, Value}; +use crate::settings::*; lazy_static! { pub static ref REDRAW_SCHEDULER: RedrawScheduler = RedrawScheduler::new(); @@ -15,28 +15,13 @@ struct RedrawSettings { extra_buffer_frames: u64, } -fn parse_changed_setting(name: &str, value: Option) -> Value { - match name { - "extra_buffer_frames" => { - let mut settings = SETTINGS.get::(); - if let Some(value) = value { - settings.extra_buffer_frames = value.as_u64().unwrap(); // TODO -- handle wrong data type - SETTINGS.set(&settings); - } - Value::from(settings.extra_buffer_frames) - } - _ => { - panic!(format!("Unknown setting: {}", name)); - } - } -} - pub fn initialize_settings() { + SETTINGS.set(&RedrawSettings { extra_buffer_frames: 60, }); - SETTINGS.add_listener("extra_buffer_frames", parse_changed_setting); + register_nvim_setting!("extra_buffer_frames", RedrawSettings::extra_buffer_frames); } pub struct RedrawScheduler { diff --git a/src/settings.rs b/src/settings.rs index f09c113..aa7d059 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -16,6 +16,85 @@ lazy_static! { pub static ref SETTINGS: Settings = Settings::new(); } +pub trait FromValue { + fn from_value(&mut self, value: Value); +} + +impl FromValue for f32 { + fn from_value(&mut self, value: Value) { + // TODO -- Warn when incorrect type + if value.is_f32() { + *self = value.as_f64().unwrap() as f32; + } + } +} + +impl FromValue for u64 { + fn from_value(&mut self, value: Value) { + // TODO -- Warn when incorrect type + if value.is_u64() { + *self = value.as_u64().unwrap(); + } + } +} + +impl FromValue for u32 { + fn from_value(&mut self, value: Value) { + // TODO -- Warn when incorrect type + if value.is_u64() { + *self = value.as_u64().unwrap() as u32; + } + } +} + +impl FromValue for i32 { + fn from_value(&mut self, value: Value) { + // TODO -- Warn when incorrect type + if value.is_i64() { + *self = value.as_i64().unwrap() as i32; + } + } +} + +impl FromValue for String { + fn from_value(&mut self, value: Value) { + // TODO -- Warn when incorrect type + if value.is_str() { + *self = String::from(value.as_str().unwrap()); + } + } +} + +impl FromValue for bool { + fn from_value(&mut self, value: Value) { + // TODO -- Warn when incorrect type + if value.is_bool() { + *self = value.as_bool().unwrap(); + } + } +} + +#[macro_export] +macro_rules! register_nvim_setting { + ($vim_setting_name: expr, $type_name:ident :: $field_name: ident) => {{ + fn update_func(value: Value) { + let mut s = SETTINGS.get::<$type_name>(); + s.$field_name.from_value(value); + SETTINGS.set(&s); + } + + fn reader_func() -> Value { + let s = SETTINGS.get::<$type_name>(); + s.$field_name.into() + } + + SETTINGS.add_handlers($vim_setting_name, update_func, reader_func); + }}; +} + +type UpdateHandlerFunc = fn(Value); +type ReaderFunc = fn()->Value; + struct SettingsObject { object: Box, } @@ -23,7 +102,8 @@ struct SettingsObject { pub struct Settings { pub neovim_arguments: Vec, settings: RwLock>, - listeners: RwLock)->Value>>, + listeners: RwLock>, + readers: RwLock>, } impl Settings { @@ -33,6 +113,8 @@ impl Settings { let mut no_idle = false; let mut buffer_frames = 1; + // TODO -- Make command line parsing work again + let neovim_arguments = std::env::args().filter(|arg| { if arg == "--log" { Logger::with_str("neovide") @@ -57,11 +139,13 @@ impl Settings { neovim_arguments, settings: RwLock::new(HashMap::new()), listeners: RwLock::new(HashMap::new()), + readers: RwLock::new(HashMap::new()), } } - pub fn add_listener(&self, property_name: &str, func: fn (&str, Option)-> Value) { - self.listeners.write().insert(String::from(property_name), func); + pub fn add_handlers(&self, property_name: &str, update_func: UpdateHandlerFunc, reader_func: ReaderFunc) { + self.listeners.write().insert(String::from(property_name), update_func); + self.readers.write().insert(String::from(property_name), reader_func); } pub fn set(&self, t: &T) { @@ -83,11 +167,11 @@ impl Settings { let variable_name = format!("neovide_{}", name.to_string()); match nvim.get_var(&variable_name).await { Ok(value) => { - self.listeners.read().get(&name).unwrap()(&name, Some(value)); + self.listeners.read().get(&name).unwrap()(value); }, Err(error) => { warn!("Initial value load failed for {}: {}", name, error); - let setting = self.listeners.read().get(&name).unwrap()(&name, None); + let setting = self.readers.read().get(&name).unwrap()(); nvim.set_var(&variable_name, setting).await.ok(); } } @@ -118,40 +202,6 @@ impl Settings { let name: Result= name.try_into(); let name = name.unwrap(); - self.listeners.read().get(&name).unwrap()(&name, Some(value)); - } - - /* - pub fn new() -> Settings { - let mut no_idle = false; - let mut buffer_frames = 1; - - let neovim_arguments = std::env::args().filter(|arg| { - if arg == "--log" { - Logger::with_str("neovide") - .log_to_file() - .rotate(Criterion::Size(10_000_000), Naming::Timestamps, Cleanup::KeepLogFiles(1)) - .start() - .expect("Could not start logger"); - false - } else if arg == "--noIdle" { - no_idle = true; - false - } else if arg == "--extraBufferFrames" { - buffer_frames = 60; - false - } else { - true - } - }).collect::>(); - - 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.insert("refresh_rate".to_string(), Setting::new_u16(60)); - - Settings { neovim_arguments, settings: Mutex::new(settings) } + self.listeners.read().get(&name).unwrap()(value); } - */ } diff --git a/src/window.rs b/src/window.rs index bbdc269..33a263a 100644 --- a/src/window.rs +++ b/src/window.rs @@ -10,11 +10,11 @@ use skulpin::sdl2::event::Event; use skulpin::sdl2::keyboard::{Mod, Keycode}; use skulpin::{RendererBuilder, Renderer as SkulpinRenderer, PresentMode, CoordinateSystem, dpis}; +use crate::settings::*; use crate::bridge::{parse_keycode, append_modifiers, BRIDGE, UiCommand}; use crate::renderer::Renderer; use crate::redraw_scheduler::REDRAW_SCHEDULER; use crate::editor::EDITOR; -use crate::settings::{SETTINGS, Value}; use crate::INITIAL_DIMENSIONS; #[derive(RustEmbed)] @@ -257,37 +257,14 @@ struct WindowSettings { no_idle: bool, } -fn parse_changed_setting(name: &str, value: Option) -> Value { - let mut settings = SETTINGS.get::(); - match name { - "refresh_rate" => { - if let Some(value) = value { - settings.refresh_rate = value.as_u64().unwrap(); // TODO -- handle wrong data type - SETTINGS.set(&settings); - } - Value::from(settings.refresh_rate) - }, - "no_idle" => { - if let Some(value) = value { - settings.no_idle = value.as_bool().unwrap(); // TODO -- handle wrong data type - SETTINGS.set(&settings); - } - Value::from(settings.no_idle) - }, - _ => { - panic!(format!("Unknown setting: {}", name)); - } - } -} - pub fn initialize_settings() { SETTINGS.set(&WindowSettings { refresh_rate: 60, no_idle: false, }); - - SETTINGS.add_listener("refresh_rate", parse_changed_setting); - SETTINGS.add_listener("no_idle", parse_changed_setting); + + register_nvim_setting!("refresh_rate", WindowSettings::refresh_rate); + register_nvim_setting!("no_idle", WindowSettings::no_idle); } pub fn ui_loop() { From 28128b56b2167a5eaf7e67710c3a85797dbbf672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20Vald=C3=A9s?= Date: Tue, 25 Feb 2020 10:46:02 +0100 Subject: [PATCH 4/6] Fixed command line parsing. Added error reporting on wrong setting data type --- src/redraw_scheduler.rs | 8 +++++++- src/settings.rs | 36 ++++++++++++++++-------------------- src/window.rs | 5 ++++- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/src/redraw_scheduler.rs b/src/redraw_scheduler.rs index 9780542..01f433f 100644 --- a/src/redraw_scheduler.rs +++ b/src/redraw_scheduler.rs @@ -16,9 +16,15 @@ struct RedrawSettings { } pub fn initialize_settings() { + + let buffer_frames = if SETTINGS.neovim_arguments.contains(&String::from("--extraBufferFrames")) { + 60 + }else{ + 1 + }; SETTINGS.set(&RedrawSettings { - extra_buffer_frames: 60, + extra_buffer_frames: buffer_frames, }); register_nvim_setting!("extra_buffer_frames", RedrawSettings::extra_buffer_frames); diff --git a/src/settings.rs b/src/settings.rs index aa7d059..62f1751 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -8,7 +8,7 @@ use nvim_rs::compat::tokio::Compat; use flexi_logger::{Logger, Criterion, Naming, Cleanup}; use tokio::process::ChildStdin; use parking_lot::RwLock; -use log::warn; +use log::{error,warn}; use crate::error_handling::ResultPanicExplanation; @@ -22,46 +22,54 @@ pub trait FromValue { impl FromValue for f32 { fn from_value(&mut self, value: Value) { - // TODO -- Warn when incorrect type if value.is_f32() { *self = value.as_f64().unwrap() as f32; + }else{ + error!("Setting expected an f32, but received {:?}", value); } + } } impl FromValue for u64 { fn from_value(&mut self, value: Value) { - // TODO -- Warn when incorrect type if value.is_u64() { *self = value.as_u64().unwrap(); + }else{ + error!("Setting expected a u64, but received {:?}", value); } + } } impl FromValue for u32 { fn from_value(&mut self, value: Value) { - // TODO -- Warn when incorrect type if value.is_u64() { *self = value.as_u64().unwrap() as u32; + }else{ + error!("Setting expected a u32, but received {:?}", value); } } } impl FromValue for i32 { fn from_value(&mut self, value: Value) { - // TODO -- Warn when incorrect type if value.is_i64() { *self = value.as_i64().unwrap() as i32; + }else{ + error!("Setting expected an i32, but received {:?}", value); } } } impl FromValue for String { fn from_value(&mut self, value: Value) { - // TODO -- Warn when incorrect type if value.is_str() { *self = String::from(value.as_str().unwrap()); + }else{ + error!("Setting expected a string, but received {:?}", value); } + } } @@ -88,7 +96,7 @@ macro_rules! register_nvim_setting { s.$field_name.into() } - SETTINGS.add_handlers($vim_setting_name, update_func, reader_func); + SETTINGS.set_setting_handlers($vim_setting_name, update_func, reader_func); }}; } @@ -110,11 +118,6 @@ impl Settings { fn new() -> Settings { - let mut no_idle = false; - let mut buffer_frames = 1; - - // TODO -- Make command line parsing work again - let neovim_arguments = std::env::args().filter(|arg| { if arg == "--log" { Logger::with_str("neovide") @@ -123,18 +126,11 @@ impl Settings { .start() .expect("Could not start logger"); false - } else if arg == "--noIdle" { - no_idle = true; - false - } else if arg == "--extraBufferFrames" { - buffer_frames = 60; - false } else { true } }).collect::>(); - Settings{ neovim_arguments, settings: RwLock::new(HashMap::new()), @@ -143,7 +139,7 @@ impl Settings { } } - pub fn add_handlers(&self, property_name: &str, update_func: UpdateHandlerFunc, reader_func: ReaderFunc) { + pub fn set_setting_handlers(&self, property_name: &str, update_func: UpdateHandlerFunc, reader_func: ReaderFunc) { self.listeners.write().insert(String::from(property_name), update_func); self.readers.write().insert(String::from(property_name), reader_func); } diff --git a/src/window.rs b/src/window.rs index 33a263a..b6c87e9 100644 --- a/src/window.rs +++ b/src/window.rs @@ -258,9 +258,12 @@ struct WindowSettings { } pub fn initialize_settings() { + + let no_idle = SETTINGS.neovim_arguments.contains(&String::from("--noIdle")); + SETTINGS.set(&WindowSettings { refresh_rate: 60, - no_idle: false, + no_idle, }); register_nvim_setting!("refresh_rate", WindowSettings::refresh_rate); From 6f4692db1bf7bbb9fa5a5143f53856f64694097b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20Vald=C3=A9s?= Date: Tue, 25 Feb 2020 17:38:49 +0100 Subject: [PATCH 5/6] Added a warning I forgot to change on last commit --- src/settings.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/settings.rs b/src/settings.rs index 62f1751..3b568a5 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -75,9 +75,10 @@ impl FromValue for String { impl FromValue for bool { fn from_value(&mut self, value: Value) { - // TODO -- Warn when incorrect type if value.is_bool() { *self = value.as_bool().unwrap(); + }else{ + error!("Setting expected a string, but received {:?}", value); } } } From 9066802fbe9abd1e06c017c72853c4fbd2bfa8c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20Vald=C3=A9s?= Date: Tue, 25 Feb 2020 18:04:31 +0100 Subject: [PATCH 6/6] Documentation, a bit of code cleaning --- src/settings.rs | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/settings.rs b/src/settings.rs index 3b568a5..7162f02 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -16,10 +16,14 @@ lazy_static! { pub static ref SETTINGS: Settings = Settings::new(); } +// Trait to allow for conversion from rmpv::Value to any other data type. +// Note: Feel free to implement this trait for custom types in each subsystem. +// The reverse conversion (MyType->Value) can be performed by implementing `From for Value` pub trait FromValue { fn from_value(&mut self, value: Value); } +// FromValue implementations for most typical types impl FromValue for f32 { fn from_value(&mut self, value: Value) { if value.is_f32() { @@ -83,15 +87,19 @@ impl FromValue for bool { } } +// Macro to register settings changed handlers. +// Note: Invocations to this macro must happen before the call to Settings::read_initial_values. #[macro_export] macro_rules! register_nvim_setting { ($vim_setting_name: expr, $type_name:ident :: $field_name: ident) => {{ + // The update func sets a new value for a setting fn update_func(value: Value) { let mut s = SETTINGS.get::<$type_name>(); s.$field_name.from_value(value); SETTINGS.set(&s); } + // The reader func retrieves the current value for a setting fn reader_func() -> Value { let s = SETTINGS.get::<$type_name>(); s.$field_name.into() @@ -101,16 +109,21 @@ macro_rules! register_nvim_setting { }}; } +// Function types to handle settings updates type UpdateHandlerFunc = fn(Value); type ReaderFunc = fn()->Value; -struct SettingsObject { - object: Box, -} +// The Settings struct acts as a global container where each of Neovide's subsystems can store +// their own settings. It will also coordinate updates between Neovide and nvim to make sure the +// settings remain consistent on both sides. +// Note: As right now we're only sending new setting values to Neovide during the +// read_initial_values call, after that point we should not modify the contents of the Settings +// struct except when prompted by an update event from nvim. Otherwise, the settings in Neovide and +// nvim will get out of sync. pub struct Settings { pub neovim_arguments: Vec, - settings: RwLock>, + settings: RwLock>>, listeners: RwLock>, readers: RwLock>, } @@ -118,7 +131,6 @@ pub struct Settings { impl Settings { fn new() -> Settings { - let neovim_arguments = std::env::args().filter(|arg| { if arg == "--log" { Logger::with_str("neovide") @@ -148,13 +160,13 @@ impl Settings { pub fn set(&self, t: &T) { let type_id : TypeId = TypeId::of::(); let t : T = (*t).clone(); - self.settings.write().insert(type_id, SettingsObject{ object: Box::new(t)}); + self.settings.write().insert(type_id, Box::new(t)); } pub fn get<'a, T: Clone + Send + Sync + 'static>(&'a self) -> T { let read_lock = self.settings.read(); let boxed = &read_lock.get(&TypeId::of::()).expect("Trying to retrieve a settings object that doesn't exist"); - let value: &T = boxed.object.downcast_ref::().expect("Attempted to extract a settings object of the wrong type"); + let value: &T = boxed.downcast_ref::().expect("Attempted to extract a settings object of the wrong type"); (*value).clone() }