From af93c54f3eee048c8d380045c03ed0c2d10362ea Mon Sep 17 00:00:00 2001 From: Keith Simmons Date: Fri, 8 Jan 2021 00:28:25 -0800 Subject: [PATCH] Settings derive macro (#439) * Added procedural macro crate * Initial derive macro implementation * Compiles for cursor settings * Derive macro working correctly * Derive macro working for all settings structs * Cleanup * Moved the binary back to the project root * remove unused utils file Co-authored-by: Tim Harding --- Cargo.lock | 9 + Cargo.toml | 6 + build.rs | 16 +- neovide-derive/Cargo.toml | 11 + neovide-derive/src/lib.rs | 77 +++++++ src/main.rs | 345 ++++++++++++++-------------- src/redraw_scheduler.rs | 32 ++- src/renderer/cursor_renderer/mod.rs | 70 ++---- src/renderer/mod.rs | 38 +-- src/settings/mod.rs | 22 +- src/window/keyboard/mod.rs | 5 +- src/window/keyboard/settings.rs | 19 +- src/window/settings.rs | 34 ++- 13 files changed, 356 insertions(+), 328 deletions(-) create mode 100644 neovide-derive/Cargo.toml create mode 100644 neovide-derive/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 7a79c40..dc9eb51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1505,6 +1505,7 @@ dependencies = [ "log", "lru", "mockall", + "neovide-derive", "nvim-rs", "parking_lot", "rand", @@ -1521,6 +1522,14 @@ dependencies = [ "winres", ] +[[package]] +name = "neovide-derive" +version = "0.1.0" +dependencies = [ + "quote 1.0.7", + "syn 1.0.54", +] + [[package]] name = "net2" version = "0.2.37" diff --git a/Cargo.toml b/Cargo.toml index 1acb926..07574be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,11 @@ edition = "2018" build = "build.rs" description = "A simple GUI for Neovim." +[workspace] +members = [ + "neovide-derive" +] + [features] default = ["sdl2"] embed-fonts = [] @@ -13,6 +18,7 @@ sdl2 = ["skulpin/skulpin_sdl2"] winit = ["skulpin/skulpin_winit"] [dependencies] +neovide-derive = { path = "neovide-derive" } euclid = "0.20.7" font-kit = "0.10.0" skribo = { git = "https://github.com/linebender/skribo" } diff --git a/build.rs b/build.rs index 4d408bc..81cf90b 100644 --- a/build.rs +++ b/build.rs @@ -1,8 +1,8 @@ -fn main() { - #[cfg(windows)] - { - let mut res = winres::WindowsResource::new(); - res.set_icon("assets/nvim.ico"); - res.compile().expect("Could not attach exe icon"); - } -} +fn main() { + #[cfg(windows)] + { + let mut res = winres::WindowsResource::new(); + res.set_icon("assets/nvim.ico"); + res.compile().expect("Could not attach exe icon"); + } +} diff --git a/neovide-derive/Cargo.toml b/neovide-derive/Cargo.toml new file mode 100644 index 0000000..983f926 --- /dev/null +++ b/neovide-derive/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "neovide-derive" +version = "0.1.0" +edition = "2018" + +[lib] +proc-macro = true + +[dependencies] +syn = "1.0" +quote = "1.0" \ No newline at end of file diff --git a/neovide-derive/src/lib.rs b/neovide-derive/src/lib.rs new file mode 100644 index 0000000..3f0e2d3 --- /dev/null +++ b/neovide-derive/src/lib.rs @@ -0,0 +1,77 @@ +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, Attribute, Data, DataStruct, DeriveInput, Error, Ident, Lit, Meta}; + +#[proc_macro_derive(SettingGroup, attributes(setting_prefix))] +pub fn setting_group(item: TokenStream) -> TokenStream { + let input = parse_macro_input!(item as DeriveInput); + let prefix = setting_prefix(input.attrs.as_ref()) + .map(|p| format!("{}_", p)) + .unwrap_or("".to_string()); + stream(input, prefix) +} + +fn stream(input: DeriveInput, prefix: String) -> TokenStream { + const ERR_MSG: &'static str = "Derive macro expects a struct"; + match input.data { + Data::Struct(ref data) => struct_stream(input.ident, prefix, data), + Data::Enum(data) => Error::new_spanned(data.enum_token, ERR_MSG) + .to_compile_error() + .into(), + Data::Union(data) => Error::new_spanned(data.union_token, ERR_MSG) + .to_compile_error() + .into(), + } +} + +fn struct_stream(name: Ident, prefix: String, data: &DataStruct) -> TokenStream { + let fragments = data.fields.iter().map(|field| match field.ident { + Some(ref ident) => { + let vim_setting_name = format!("{}{}", prefix, ident); + quote! {{ + fn update_func(value: rmpv::Value) { + let mut s = crate::settings::SETTINGS.get::<#name>(); + s.#ident.from_value(value); + crate::settings::SETTINGS.set(&s); + } + + fn reader_func() -> rmpv::Value { + let s = crate::settings::SETTINGS.get::<#name>(); + s.#ident.into() + } + + crate::settings::SETTINGS.set_setting_handlers( + #vim_setting_name, + update_func, + reader_func + ); + }} + } + None => { + Error::new_spanned(field.colon_token, "Expected named struct fields").to_compile_error() + } + }); + let expanded = quote! { + impl #name { + pub fn register() { + let s: Self = Default::default(); + crate::settings::SETTINGS.set(&s); + #(#fragments)* + } + } + }; + TokenStream::from(expanded) +} + +fn setting_prefix(attrs: &[Attribute]) -> Option { + for attr in attrs.iter() { + if let Ok(Meta::NameValue(name_value)) = attr.parse_meta() { + if name_value.path.is_ident("setting_prefix") { + if let Lit::Str(literal) = name_value.lit { + return Some(literal.value()); + } + } + } + } + None +} diff --git a/src/main.rs b/src/main.rs index d682bb5..85b352a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,172 +1,173 @@ -#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] - -#[macro_use] -mod settings; - -mod bridge; -mod editor; -mod error_handling; -mod redraw_scheduler; -mod renderer; -mod window; - -#[macro_use] -extern crate derive_new; -#[macro_use] -extern crate rust_embed; -#[macro_use] -extern crate lazy_static; - -use std::process; -use std::sync::atomic::AtomicBool; -use std::sync::mpsc::channel; -use std::sync::Arc; - -use crossfire::mpsc::unbounded_future; - -use window::window_geometry; - -use bridge::start_bridge; -use editor::start_editor; -use window::create_window; - -pub const INITIAL_DIMENSIONS: (u64, u64) = (100, 50); - -fn main() { - // ----------- - // | DATA FLOW | - // ----------- - // - // Data flows in a circular motion via channels. This allows each component to handle and - // process data on their own thread while not blocking the other components from processing. - // - // This way Neovim continues to produce events, the window doesn't freeze and queues up ui - // commands, and the editor can do the processing necessary to handle the UI events - // effectively. - // - // BRIDGE - // V REDRAW EVENT - // EDITOR - // V DRAW COMMAND - // WINDOW - // V UI COMMAND - // BRIDGE - // - // BRIDGE: - // The bridge is responsible for the connection to the neovim process itself. It is in charge - // of starting and communicating to and from the process. - // - // REDRAW EVENT: - // Redraw events are direct events from the neovim process meant to specify how the editor - // should be drawn to the screen. They also include other things such as whether the mouse is - // enabled. The bridge takes these events, filters out some of them meant only for - // filtering, and forwards them to the editor. - // - // EDITOR: - // The editor is responsible for processing and transforming redraw events into something - // more readily renderable. Ligature support and multi window management requires some - // significant preprocessing of the redraw events in order to capture what exactly should get - // drawn where. Futher this step takes a bit of processing power to accomplish, so it is done - // on it's own thread. Ideally heavily computationally expensive tasks should be done in the - // editor. - // - // DRAW COMMAND: - // The draw commands are distilled render information describing actions to be done at the - // window by window level. - // - // WINDOW: - // The window is responsible for rendering and gathering input events from the user. This - // inncludes taking the draw commands from the editor and turning them into pixels on the - // screen. The ui commands are then forwarded back to the BRIDGE to convert them into - // commands for neovim to handle properly. - // - // UI COMMAND: - // The ui commands are things like text input/key bindings, outer window resizes, and mouse - // inputs. - // - // ------------------ - // | Other Components | - // ------------------ - // - // Neovide also includes some other systems which are globally available via lazy static - // instantiations. - // - // SETTINGS: - // The settings system is live updated from global variables in neovim with the prefix - // "neovide". They allow us to configure and manage the functionality of neovide from neovim - // init scripts and variables. - // - // REDRAW SCHEDULER: - // The redraw scheduler is a simple system in charge of deciding if the renderer should draw - // another frame next frame, or if it can safely skip drawing to save battery and cpu power. - // Multiple other parts of the app "queue_next_frame" function to ensure animations continue - // properly or updates to the graphics are pushed to the screen. - - if let Err(err) = window_geometry() { - eprintln!("{}", err); - process::exit(1); - }; - - #[cfg(target_os = "macos")] - { - // incase of app bundle, we can just pass --disowned option straight away to bypass this check - #[cfg(not(debug_assertions))] - if !std::env::args().any(|f| f == "--disowned") { - if let Ok(curr_exe) = std::env::current_exe() { - assert!(std::process::Command::new(curr_exe) - .args(std::env::args().skip(1)) - .arg("--disowned") - .spawn() - .is_ok()); - std::process::exit(0); - } else { - eprintln!("error in disowning process, cannot obtain the path for the current executable, continuing without disowning..."); - } - } - - use std::env; - if env::var_os("TERM").is_none() { - let mut profile_path = dirs::home_dir().unwrap(); - profile_path.push(".profile"); - let shell = env::var("SHELL").unwrap(); - let cmd = format!( - "(source /etc/profile && source {} && echo $PATH)", - profile_path.to_str().unwrap() - ); - if let Ok(path) = process::Command::new(shell).arg("-c").arg(cmd).output() { - env::set_var("PATH", std::str::from_utf8(&path.stdout).unwrap()); - } - } - } - - window::initialize_settings(); - redraw_scheduler::initialize_settings(); - renderer::initialize_settings(); - renderer::cursor_renderer::initialize_settings(); - - let running = Arc::new(AtomicBool::new(true)); - - let (redraw_event_sender, redraw_event_receiver) = unbounded_future(); - let (batched_draw_command_sender, batched_draw_command_receiver) = channel(); - let (ui_command_sender, ui_command_receiver) = unbounded_future(); - let (window_command_sender, window_command_receiver) = channel(); - - // We need to keep the bridge reference around to prevent the tokio runtime from getting freed - let _bridge = start_bridge( - ui_command_sender.clone(), - ui_command_receiver, - redraw_event_sender, - running.clone(), - ); - start_editor( - redraw_event_receiver, - batched_draw_command_sender, - window_command_sender, - ); - create_window( - batched_draw_command_receiver, - window_command_receiver, - ui_command_sender, - running, - ); -} +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +#[macro_use] +extern crate neovide_derive; + +mod bridge; +mod editor; +mod error_handling; +mod redraw_scheduler; +mod renderer; +mod settings; +mod window; + +#[macro_use] +extern crate derive_new; +#[macro_use] +extern crate rust_embed; +#[macro_use] +extern crate lazy_static; + +use std::{ + process, + sync::{atomic::AtomicBool, mpsc::channel, Arc}, +}; + +use crossfire::mpsc::unbounded_future; + +use bridge::start_bridge; +use editor::start_editor; +use renderer::{cursor_renderer::CursorSettings, RendererSettings}; +use window::{create_window, window_geometry, KeyboardSettings, WindowSettings}; + +pub const INITIAL_DIMENSIONS: (u64, u64) = (100, 50); + +fn main() { + // ----------- + // | DATA FLOW | + // ----------- + // + // Data flows in a circular motion via channels. This allows each component to handle and + // process data on their own thread while not blocking the other components from processing. + // + // This way Neovim continues to produce events, the window doesn't freeze and queues up ui + // commands, and the editor can do the processing necessary to handle the UI events + // effectively. + // + // BRIDGE + // V REDRAW EVENT + // EDITOR + // V DRAW COMMAND + // WINDOW + // V UI COMMAND + // BRIDGE + // + // BRIDGE: + // The bridge is responsible for the connection to the neovim process itself. It is in charge + // of starting and communicating to and from the process. + // + // REDRAW EVENT: + // Redraw events are direct events from the neovim process meant to specify how the editor + // should be drawn to the screen. They also include other things such as whether the mouse is + // enabled. The bridge takes these events, filters out some of them meant only for + // filtering, and forwards them to the editor. + // + // EDITOR: + // The editor is responsible for processing and transforming redraw events into something + // more readily renderable. Ligature support and multi window management requires some + // significant preprocessing of the redraw events in order to capture what exactly should get + // drawn where. Futher this step takes a bit of processing power to accomplish, so it is done + // on it's own thread. Ideally heavily computationally expensive tasks should be done in the + // editor. + // + // DRAW COMMAND: + // The draw commands are distilled render information describing actions to be done at the + // window by window level. + // + // WINDOW: + // The window is responsible for rendering and gathering input events from the user. This + // inncludes taking the draw commands from the editor and turning them into pixels on the + // screen. The ui commands are then forwarded back to the BRIDGE to convert them into + // commands for neovim to handle properly. + // + // UI COMMAND: + // The ui commands are things like text input/key bindings, outer window resizes, and mouse + // inputs. + // + // ------------------ + // | Other Components | + // ------------------ + // + // Neovide also includes some other systems which are globally available via lazy static + // instantiations. + // + // SETTINGS: + // The settings system is live updated from global variables in neovim with the prefix + // "neovide". They allow us to configure and manage the functionality of neovide from neovim + // init scripts and variables. + // + // REDRAW SCHEDULER: + // The redraw scheduler is a simple system in charge of deciding if the renderer should draw + // another frame next frame, or if it can safely skip drawing to save battery and cpu power. + // Multiple other parts of the app "queue_next_frame" function to ensure animations continue + // properly or updates to the graphics are pushed to the screen. + + if let Err(err) = window_geometry() { + eprintln!("{}", err); + process::exit(1); + }; + + #[cfg(target_os = "macos")] + { + // incase of app bundle, we can just pass --disowned option straight away to bypass this check + #[cfg(not(debug_assertions))] + if !std::env::args().any(|f| f == "--disowned") { + if let Ok(curr_exe) = std::env::current_exe() { + assert!(std::process::Command::new(curr_exe) + .args(std::env::args().skip(1)) + .arg("--disowned") + .spawn() + .is_ok()); + std::process::exit(0); + } else { + eprintln!("error in disowning process, cannot obtain the path for the current executable, continuing without disowning..."); + } + } + + use std::env; + if env::var_os("TERM").is_none() { + let mut profile_path = dirs::home_dir().unwrap(); + profile_path.push(".profile"); + let shell = env::var("SHELL").unwrap(); + let cmd = format!( + "(source /etc/profile && source {} && echo $PATH)", + profile_path.to_str().unwrap() + ); + if let Ok(path) = process::Command::new(shell).arg("-c").arg(cmd).output() { + env::set_var("PATH", std::str::from_utf8(&path.stdout).unwrap()); + } + } + } + + KeyboardSettings::register(); + WindowSettings::register(); + redraw_scheduler::RedrawSettings::register(); + RendererSettings::register(); + CursorSettings::register(); + + let running = Arc::new(AtomicBool::new(true)); + + let (redraw_event_sender, redraw_event_receiver) = unbounded_future(); + let (batched_draw_command_sender, batched_draw_command_receiver) = channel(); + let (ui_command_sender, ui_command_receiver) = unbounded_future(); + let (window_command_sender, window_command_receiver) = channel(); + + // We need to keep the bridge reference around to prevent the tokio runtime from getting freed + let _bridge = start_bridge( + ui_command_sender.clone(), + ui_command_receiver, + redraw_event_sender, + running.clone(), + ); + start_editor( + redraw_event_receiver, + batched_draw_command_sender, + window_command_sender, + ); + create_window( + batched_draw_command_receiver, + window_command_receiver, + ui_command_sender, + running, + ); +} diff --git a/src/redraw_scheduler.rs b/src/redraw_scheduler.rs index 46cf56b..ec261d6 100644 --- a/src/redraw_scheduler.rs +++ b/src/redraw_scheduler.rs @@ -10,26 +10,24 @@ lazy_static! { pub static ref REDRAW_SCHEDULER: RedrawScheduler = RedrawScheduler::new(); } -#[derive(Clone)] -struct RedrawSettings { +#[derive(Clone, SettingGroup)] +pub struct RedrawSettings { extra_buffer_frames: u64, } -pub fn initialize_settings() { - let buffer_frames = if SETTINGS - .neovim_arguments - .contains(&String::from("--extraBufferFrames")) - { - 60 - } else { - 1 - }; - - SETTINGS.set(&RedrawSettings { - extra_buffer_frames: buffer_frames, - }); - - register_nvim_setting!("extra_buffer_frames", RedrawSettings::extra_buffer_frames); +impl Default for RedrawSettings { + fn default() -> Self { + Self { + extra_buffer_frames: if SETTINGS + .neovim_arguments + .contains(&"--extraBufferFrames".to_string()) + { + 60 + } else { + 1 + }, + } + } } pub struct RedrawScheduler { diff --git a/src/renderer/cursor_renderer/mod.rs b/src/renderer/cursor_renderer/mod.rs index c78f8ce..c548c94 100644 --- a/src/renderer/cursor_renderer/mod.rs +++ b/src/renderer/cursor_renderer/mod.rs @@ -3,6 +3,7 @@ mod cursor_vfx; use std::collections::HashMap; +// use neovide_derive::SettingGroup; use skulpin::skia_safe::{Canvas, Paint, Path, Point}; use super::RenderedWindow; @@ -11,7 +12,7 @@ use crate::editor::{Colors, Cursor, CursorShape}; use crate::redraw_scheduler::REDRAW_SCHEDULER; use crate::renderer::animation_utils::*; use crate::renderer::CachingShaper; -use crate::settings::*; +use crate::settings::{FromValue, SETTINGS}; use blink::*; @@ -19,9 +20,8 @@ const DEFAULT_CELL_PERCENTAGE: f32 = 1.0 / 8.0; const STANDARD_CORNERS: &[(f32, f32); 4] = &[(-0.5, -0.5), (0.5, -0.5), (0.5, 0.5), (-0.5, 0.5)]; -// ---------------------------------------------------------------------------- - -#[derive(Clone)] +#[setting_prefix = "cursor"] +#[derive(Clone, SettingGroup)] pub struct CursorSettings { antialiasing: bool, animation_length: f32, @@ -36,54 +36,24 @@ pub struct CursorSettings { vfx_particle_curl: f32, } -pub fn initialize_settings() { - SETTINGS.set(&CursorSettings { - antialiasing: true, - animation_length: 0.13, - animate_in_insert_mode: true, - trail_size: 0.7, - vfx_mode: cursor_vfx::VfxMode::Disabled, - vfx_opacity: 200.0, - vfx_particle_lifetime: 1.2, - vfx_particle_density: 7.0, - vfx_particle_speed: 10.0, - vfx_particle_phase: 1.5, - vfx_particle_curl: 1.0, - }); - - register_nvim_setting!("cursor_antialiasing", CursorSettings::antialiasing); - register_nvim_setting!( - "cursor_animate_in_insert_mode", - CursorSettings::animate_in_insert_mode - ); - register_nvim_setting!("cursor_animation_length", CursorSettings::animation_length); - register_nvim_setting!("cursor_trail_size", CursorSettings::trail_size); - register_nvim_setting!("cursor_vfx_mode", CursorSettings::vfx_mode); - register_nvim_setting!("cursor_vfx_opacity", CursorSettings::vfx_opacity); - register_nvim_setting!( - "cursor_vfx_particle_lifetime", - CursorSettings::vfx_particle_lifetime - ); - register_nvim_setting!( - "cursor_vfx_particle_density", - CursorSettings::vfx_particle_density - ); - register_nvim_setting!( - "cursor_vfx_particle_speed", - CursorSettings::vfx_particle_speed - ); - register_nvim_setting!( - "cursor_vfx_particle_phase", - CursorSettings::vfx_particle_phase - ); - register_nvim_setting!( - "cursor_vfx_particle_curl", - CursorSettings::vfx_particle_curl - ); +impl Default for CursorSettings { + fn default() -> Self { + CursorSettings { + antialiasing: true, + animation_length: 0.13, + animate_in_insert_mode: true, + trail_size: 0.7, + vfx_mode: cursor_vfx::VfxMode::Disabled, + vfx_opacity: 200.0, + vfx_particle_lifetime: 1.2, + vfx_particle_density: 7.0, + vfx_particle_speed: 10.0, + vfx_particle_phase: 1.5, + vfx_particle_curl: 1.0, + } + } } -// ---------------------------------------------------------------------------- - #[derive(Debug, Clone)] pub struct Corner { start_position: Point, diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index 169db7a..064e6cd 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -21,9 +21,8 @@ use crate::editor::{Colors, DrawCommand, Style, WindowDrawCommand}; use crate::settings::*; use cursor_renderer::CursorRenderer; -// ---------------------------------------------------------------------------- - -#[derive(Clone)] +#[setting_prefix = "window"] +#[derive(Clone, SettingGroup)] pub struct RendererSettings { position_animation_length: f32, scroll_animation_length: f32, @@ -31,31 +30,17 @@ pub struct RendererSettings { floating_blur: bool, } -pub fn initialize_settings() { - SETTINGS.set(&RendererSettings { - position_animation_length: 0.15, - scroll_animation_length: 0.3, - floating_opacity: 0.7, - floating_blur: true, - }); - - register_nvim_setting!( - "window_position_animation_length", - RendererSettings::position_animation_length - ); - register_nvim_setting!( - "window_scroll_animation_length", - RendererSettings::scroll_animation_length - ); - register_nvim_setting!( - "floating_window_opacity", - RendererSettings::floating_opacity - ); - register_nvim_setting!("floating_window_blur", RendererSettings::floating_blur); +impl Default for RendererSettings { + fn default() -> Self { + Self { + position_animation_length: 0.15, + scroll_animation_length: 0.3, + floating_opacity: 0.7, + floating_blur: true, + } + } } -// ---------------------------------------------------------------------------- - pub struct Renderer { rendered_windows: HashMap, cursor_renderer: CursorRenderer, @@ -90,7 +75,6 @@ impl Renderer { Renderer { rendered_windows, cursor_renderer, - current_mode, paint, shaper, diff --git a/src/settings/mod.rs b/src/settings/mod.rs index 9529584..988957e 100644 --- a/src/settings/mod.rs +++ b/src/settings/mod.rs @@ -20,26 +20,8 @@ lazy_static! { pub static ref SETTINGS: Settings = Settings::new(); } -// 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() - } - - SETTINGS.set_setting_handlers($vim_setting_name, update_func, reader_func); - }}; +pub trait SettingGroup { + fn register(&self); } // Function types to handle settings updates diff --git a/src/window/keyboard/mod.rs b/src/window/keyboard/mod.rs index c5dc126..6087221 100644 --- a/src/window/keyboard/mod.rs +++ b/src/window/keyboard/mod.rs @@ -6,10 +6,7 @@ mod token; use crate::settings::SETTINGS; pub use self::{ - layout::KeyboardLayout, - modifiers::Modifiers, - settings::{initialize_settings, KeyboardSettings}, - token::Token, + layout::KeyboardLayout, modifiers::Modifiers, settings::KeyboardSettings, token::Token, }; type KeycodeToTokenFn = fn(T, bool) -> Option>; diff --git a/src/window/keyboard/settings.rs b/src/window/keyboard/settings.rs index 1b200a8..1b196cc 100644 --- a/src/window/keyboard/settings.rs +++ b/src/window/keyboard/settings.rs @@ -1,17 +1,16 @@ use super::KeyboardLayout; -use crate::{ - register_nvim_setting, - settings::{FromValue, Value, SETTINGS}, -}; +use crate::settings::FromValue; -#[derive(Clone)] +#[setting_prefix = "keyboard"] +#[derive(Clone, SettingGroup)] pub struct KeyboardSettings { pub layout: KeyboardLayout, } -pub fn initialize_settings() { - SETTINGS.set(&KeyboardSettings { - layout: KeyboardLayout::Qwerty, - }); - register_nvim_setting!("keyboard_layout", KeyboardSettings::layout); +impl Default for KeyboardSettings { + fn default() -> Self { + Self { + layout: KeyboardLayout::Qwerty, + } + } } diff --git a/src/window/settings.rs b/src/window/settings.rs index c055be4..f52d65d 100644 --- a/src/window/settings.rs +++ b/src/window/settings.rs @@ -1,7 +1,8 @@ -use super::keyboard::initialize_settings as keyboard_initialize_settings; use crate::settings::*; -#[derive(Clone)] +pub use super::keyboard::KeyboardSettings; + +#[derive(Clone, SettingGroup)] pub struct WindowSettings { pub refresh_rate: u64, pub transparency: f32, @@ -9,22 +10,15 @@ pub struct WindowSettings { pub fullscreen: bool, } -pub fn initialize_settings() { - let no_idle = SETTINGS - .neovim_arguments - .contains(&String::from("--noIdle")); - - SETTINGS.set(&WindowSettings { - refresh_rate: 60, - transparency: 1.0, - no_idle, - fullscreen: false, - }); - - register_nvim_setting!("refresh_rate", WindowSettings::refresh_rate); - register_nvim_setting!("transparency", WindowSettings::transparency); - register_nvim_setting!("no_idle", WindowSettings::no_idle); - register_nvim_setting!("fullscreen", WindowSettings::fullscreen); - - keyboard_initialize_settings(); +impl Default for WindowSettings { + fn default() -> Self { + Self { + refresh_rate: 60, + transparency: 1.0, + no_idle: SETTINGS + .neovim_arguments + .contains(&String::from("--noIdle")), + fullscreen: false, + } + } }