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 <Tim@TimHarding.co>
macos-click-through
Keith Simmons 4 years ago committed by GitHub
parent 33f6a4b914
commit af93c54f3e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

9
Cargo.lock generated

@ -1505,6 +1505,7 @@ dependencies = [
"log", "log",
"lru", "lru",
"mockall", "mockall",
"neovide-derive",
"nvim-rs", "nvim-rs",
"parking_lot", "parking_lot",
"rand", "rand",
@ -1521,6 +1522,14 @@ dependencies = [
"winres", "winres",
] ]
[[package]]
name = "neovide-derive"
version = "0.1.0"
dependencies = [
"quote 1.0.7",
"syn 1.0.54",
]
[[package]] [[package]]
name = "net2" name = "net2"
version = "0.2.37" version = "0.2.37"

@ -6,6 +6,11 @@ edition = "2018"
build = "build.rs" build = "build.rs"
description = "A simple GUI for Neovim." description = "A simple GUI for Neovim."
[workspace]
members = [
"neovide-derive"
]
[features] [features]
default = ["sdl2"] default = ["sdl2"]
embed-fonts = [] embed-fonts = []
@ -13,6 +18,7 @@ sdl2 = ["skulpin/skulpin_sdl2"]
winit = ["skulpin/skulpin_winit"] winit = ["skulpin/skulpin_winit"]
[dependencies] [dependencies]
neovide-derive = { path = "neovide-derive" }
euclid = "0.20.7" euclid = "0.20.7"
font-kit = "0.10.0" font-kit = "0.10.0"
skribo = { git = "https://github.com/linebender/skribo" } skribo = { git = "https://github.com/linebender/skribo" }

@ -1,8 +1,8 @@
fn main() { fn main() {
#[cfg(windows)] #[cfg(windows)]
{ {
let mut res = winres::WindowsResource::new(); let mut res = winres::WindowsResource::new();
res.set_icon("assets/nvim.ico"); res.set_icon("assets/nvim.ico");
res.compile().expect("Could not attach exe icon"); res.compile().expect("Could not attach exe icon");
} }
} }

@ -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"

@ -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<String> {
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
}

@ -1,172 +1,173 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
#[macro_use] #[macro_use]
mod settings; extern crate neovide_derive;
mod bridge; mod bridge;
mod editor; mod editor;
mod error_handling; mod error_handling;
mod redraw_scheduler; mod redraw_scheduler;
mod renderer; mod renderer;
mod window; mod settings;
mod window;
#[macro_use]
extern crate derive_new; #[macro_use]
#[macro_use] extern crate derive_new;
extern crate rust_embed; #[macro_use]
#[macro_use] extern crate rust_embed;
extern crate lazy_static; #[macro_use]
extern crate lazy_static;
use std::process;
use std::sync::atomic::AtomicBool; use std::{
use std::sync::mpsc::channel; process,
use std::sync::Arc; sync::{atomic::AtomicBool, mpsc::channel, Arc},
};
use crossfire::mpsc::unbounded_future;
use crossfire::mpsc::unbounded_future;
use window::window_geometry;
use bridge::start_bridge;
use bridge::start_bridge; use editor::start_editor;
use editor::start_editor; use renderer::{cursor_renderer::CursorSettings, RendererSettings};
use window::create_window; use window::{create_window, window_geometry, KeyboardSettings, WindowSettings};
pub const INITIAL_DIMENSIONS: (u64, u64) = (100, 50); pub const INITIAL_DIMENSIONS: (u64, u64) = (100, 50);
fn main() { fn main() {
// ----------- // -----------
// | DATA FLOW | // | DATA FLOW |
// ----------- // -----------
// //
// Data flows in a circular motion via channels. This allows each component to handle and // 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. // 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 // 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 // commands, and the editor can do the processing necessary to handle the UI events
// effectively. // effectively.
// //
// BRIDGE // BRIDGE
// V REDRAW EVENT // V REDRAW EVENT
// EDITOR // EDITOR
// V DRAW COMMAND // V DRAW COMMAND
// WINDOW // WINDOW
// V UI COMMAND // V UI COMMAND
// BRIDGE // BRIDGE
// //
// BRIDGE: // BRIDGE:
// The bridge is responsible for the connection to the neovim process itself. It is in charge // 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. // of starting and communicating to and from the process.
// //
// REDRAW EVENT: // REDRAW EVENT:
// Redraw events are direct events from the neovim process meant to specify how the editor // 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 // 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 // enabled. The bridge takes these events, filters out some of them meant only for
// filtering, and forwards them to the editor. // filtering, and forwards them to the editor.
// //
// EDITOR: // EDITOR:
// The editor is responsible for processing and transforming redraw events into something // The editor is responsible for processing and transforming redraw events into something
// more readily renderable. Ligature support and multi window management requires some // 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 // 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 // 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 // on it's own thread. Ideally heavily computationally expensive tasks should be done in the
// editor. // editor.
// //
// DRAW COMMAND: // DRAW COMMAND:
// The draw commands are distilled render information describing actions to be done at the // The draw commands are distilled render information describing actions to be done at the
// window by window level. // window by window level.
// //
// WINDOW: // WINDOW:
// The window is responsible for rendering and gathering input events from the user. This // 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 // 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 // screen. The ui commands are then forwarded back to the BRIDGE to convert them into
// commands for neovim to handle properly. // commands for neovim to handle properly.
// //
// UI COMMAND: // UI COMMAND:
// The ui commands are things like text input/key bindings, outer window resizes, and mouse // The ui commands are things like text input/key bindings, outer window resizes, and mouse
// inputs. // inputs.
// //
// ------------------ // ------------------
// | Other Components | // | Other Components |
// ------------------ // ------------------
// //
// Neovide also includes some other systems which are globally available via lazy static // Neovide also includes some other systems which are globally available via lazy static
// instantiations. // instantiations.
// //
// SETTINGS: // SETTINGS:
// The settings system is live updated from global variables in neovim with the prefix // 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 // "neovide". They allow us to configure and manage the functionality of neovide from neovim
// init scripts and variables. // init scripts and variables.
// //
// REDRAW SCHEDULER: // REDRAW SCHEDULER:
// The redraw scheduler is a simple system in charge of deciding if the renderer should draw // 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. // 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 // 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. // properly or updates to the graphics are pushed to the screen.
if let Err(err) = window_geometry() { if let Err(err) = window_geometry() {
eprintln!("{}", err); eprintln!("{}", err);
process::exit(1); process::exit(1);
}; };
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
{ {
// incase of app bundle, we can just pass --disowned option straight away to bypass this check // incase of app bundle, we can just pass --disowned option straight away to bypass this check
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
if !std::env::args().any(|f| f == "--disowned") { if !std::env::args().any(|f| f == "--disowned") {
if let Ok(curr_exe) = std::env::current_exe() { if let Ok(curr_exe) = std::env::current_exe() {
assert!(std::process::Command::new(curr_exe) assert!(std::process::Command::new(curr_exe)
.args(std::env::args().skip(1)) .args(std::env::args().skip(1))
.arg("--disowned") .arg("--disowned")
.spawn() .spawn()
.is_ok()); .is_ok());
std::process::exit(0); std::process::exit(0);
} else { } else {
eprintln!("error in disowning process, cannot obtain the path for the current executable, continuing without disowning..."); eprintln!("error in disowning process, cannot obtain the path for the current executable, continuing without disowning...");
} }
} }
use std::env; use std::env;
if env::var_os("TERM").is_none() { if env::var_os("TERM").is_none() {
let mut profile_path = dirs::home_dir().unwrap(); let mut profile_path = dirs::home_dir().unwrap();
profile_path.push(".profile"); profile_path.push(".profile");
let shell = env::var("SHELL").unwrap(); let shell = env::var("SHELL").unwrap();
let cmd = format!( let cmd = format!(
"(source /etc/profile && source {} && echo $PATH)", "(source /etc/profile && source {} && echo $PATH)",
profile_path.to_str().unwrap() profile_path.to_str().unwrap()
); );
if let Ok(path) = process::Command::new(shell).arg("-c").arg(cmd).output() { if let Ok(path) = process::Command::new(shell).arg("-c").arg(cmd).output() {
env::set_var("PATH", std::str::from_utf8(&path.stdout).unwrap()); env::set_var("PATH", std::str::from_utf8(&path.stdout).unwrap());
} }
} }
} }
window::initialize_settings(); KeyboardSettings::register();
redraw_scheduler::initialize_settings(); WindowSettings::register();
renderer::initialize_settings(); redraw_scheduler::RedrawSettings::register();
renderer::cursor_renderer::initialize_settings(); RendererSettings::register();
CursorSettings::register();
let running = Arc::new(AtomicBool::new(true));
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 (redraw_event_sender, redraw_event_receiver) = unbounded_future();
let (ui_command_sender, ui_command_receiver) = unbounded_future(); let (batched_draw_command_sender, batched_draw_command_receiver) = channel();
let (window_command_sender, window_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( // We need to keep the bridge reference around to prevent the tokio runtime from getting freed
ui_command_sender.clone(), let _bridge = start_bridge(
ui_command_receiver, ui_command_sender.clone(),
redraw_event_sender, ui_command_receiver,
running.clone(), redraw_event_sender,
); running.clone(),
start_editor( );
redraw_event_receiver, start_editor(
batched_draw_command_sender, redraw_event_receiver,
window_command_sender, batched_draw_command_sender,
); window_command_sender,
create_window( );
batched_draw_command_receiver, create_window(
window_command_receiver, batched_draw_command_receiver,
ui_command_sender, window_command_receiver,
running, ui_command_sender,
); running,
} );
}

@ -10,26 +10,24 @@ lazy_static! {
pub static ref REDRAW_SCHEDULER: RedrawScheduler = RedrawScheduler::new(); pub static ref REDRAW_SCHEDULER: RedrawScheduler = RedrawScheduler::new();
} }
#[derive(Clone)] #[derive(Clone, SettingGroup)]
struct RedrawSettings { pub struct RedrawSettings {
extra_buffer_frames: u64, extra_buffer_frames: u64,
} }
pub fn initialize_settings() { impl Default for RedrawSettings {
let buffer_frames = if SETTINGS fn default() -> Self {
.neovim_arguments Self {
.contains(&String::from("--extraBufferFrames")) extra_buffer_frames: if SETTINGS
{ .neovim_arguments
60 .contains(&"--extraBufferFrames".to_string())
} else { {
1 60
}; } else {
1
SETTINGS.set(&RedrawSettings { },
extra_buffer_frames: buffer_frames, }
}); }
register_nvim_setting!("extra_buffer_frames", RedrawSettings::extra_buffer_frames);
} }
pub struct RedrawScheduler { pub struct RedrawScheduler {

@ -3,6 +3,7 @@ mod cursor_vfx;
use std::collections::HashMap; use std::collections::HashMap;
// use neovide_derive::SettingGroup;
use skulpin::skia_safe::{Canvas, Paint, Path, Point}; use skulpin::skia_safe::{Canvas, Paint, Path, Point};
use super::RenderedWindow; use super::RenderedWindow;
@ -11,7 +12,7 @@ use crate::editor::{Colors, Cursor, CursorShape};
use crate::redraw_scheduler::REDRAW_SCHEDULER; use crate::redraw_scheduler::REDRAW_SCHEDULER;
use crate::renderer::animation_utils::*; use crate::renderer::animation_utils::*;
use crate::renderer::CachingShaper; use crate::renderer::CachingShaper;
use crate::settings::*; use crate::settings::{FromValue, SETTINGS};
use blink::*; 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)]; const STANDARD_CORNERS: &[(f32, f32); 4] = &[(-0.5, -0.5), (0.5, -0.5), (0.5, 0.5), (-0.5, 0.5)];
// ---------------------------------------------------------------------------- #[setting_prefix = "cursor"]
#[derive(Clone, SettingGroup)]
#[derive(Clone)]
pub struct CursorSettings { pub struct CursorSettings {
antialiasing: bool, antialiasing: bool,
animation_length: f32, animation_length: f32,
@ -36,54 +36,24 @@ pub struct CursorSettings {
vfx_particle_curl: f32, vfx_particle_curl: f32,
} }
pub fn initialize_settings() { impl Default for CursorSettings {
SETTINGS.set(&CursorSettings { fn default() -> Self {
antialiasing: true, CursorSettings {
animation_length: 0.13, antialiasing: true,
animate_in_insert_mode: true, animation_length: 0.13,
trail_size: 0.7, animate_in_insert_mode: true,
vfx_mode: cursor_vfx::VfxMode::Disabled, trail_size: 0.7,
vfx_opacity: 200.0, vfx_mode: cursor_vfx::VfxMode::Disabled,
vfx_particle_lifetime: 1.2, vfx_opacity: 200.0,
vfx_particle_density: 7.0, vfx_particle_lifetime: 1.2,
vfx_particle_speed: 10.0, vfx_particle_density: 7.0,
vfx_particle_phase: 1.5, vfx_particle_speed: 10.0,
vfx_particle_curl: 1.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
);
} }
// ----------------------------------------------------------------------------
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Corner { pub struct Corner {
start_position: Point, start_position: Point,

@ -21,9 +21,8 @@ use crate::editor::{Colors, DrawCommand, Style, WindowDrawCommand};
use crate::settings::*; use crate::settings::*;
use cursor_renderer::CursorRenderer; use cursor_renderer::CursorRenderer;
// ---------------------------------------------------------------------------- #[setting_prefix = "window"]
#[derive(Clone, SettingGroup)]
#[derive(Clone)]
pub struct RendererSettings { pub struct RendererSettings {
position_animation_length: f32, position_animation_length: f32,
scroll_animation_length: f32, scroll_animation_length: f32,
@ -31,31 +30,17 @@ pub struct RendererSettings {
floating_blur: bool, floating_blur: bool,
} }
pub fn initialize_settings() { impl Default for RendererSettings {
SETTINGS.set(&RendererSettings { fn default() -> Self {
position_animation_length: 0.15, Self {
scroll_animation_length: 0.3, position_animation_length: 0.15,
floating_opacity: 0.7, scroll_animation_length: 0.3,
floating_blur: true, 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);
} }
// ----------------------------------------------------------------------------
pub struct Renderer { pub struct Renderer {
rendered_windows: HashMap<u64, RenderedWindow>, rendered_windows: HashMap<u64, RenderedWindow>,
cursor_renderer: CursorRenderer, cursor_renderer: CursorRenderer,
@ -90,7 +75,6 @@ impl Renderer {
Renderer { Renderer {
rendered_windows, rendered_windows,
cursor_renderer, cursor_renderer,
current_mode, current_mode,
paint, paint,
shaper, shaper,

@ -20,26 +20,8 @@ lazy_static! {
pub static ref SETTINGS: Settings = Settings::new(); pub static ref SETTINGS: Settings = Settings::new();
} }
// Macro to register settings changed handlers. pub trait SettingGroup {
// Note: Invocations to this macro must happen before the call to Settings::read_initial_values. fn register(&self);
#[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);
}};
} }
// Function types to handle settings updates // Function types to handle settings updates

@ -6,10 +6,7 @@ mod token;
use crate::settings::SETTINGS; use crate::settings::SETTINGS;
pub use self::{ pub use self::{
layout::KeyboardLayout, layout::KeyboardLayout, modifiers::Modifiers, settings::KeyboardSettings, token::Token,
modifiers::Modifiers,
settings::{initialize_settings, KeyboardSettings},
token::Token,
}; };
type KeycodeToTokenFn<T> = fn(T, bool) -> Option<Token<'static>>; type KeycodeToTokenFn<T> = fn(T, bool) -> Option<Token<'static>>;

@ -1,17 +1,16 @@
use super::KeyboardLayout; use super::KeyboardLayout;
use crate::{ use crate::settings::FromValue;
register_nvim_setting,
settings::{FromValue, Value, SETTINGS},
};
#[derive(Clone)] #[setting_prefix = "keyboard"]
#[derive(Clone, SettingGroup)]
pub struct KeyboardSettings { pub struct KeyboardSettings {
pub layout: KeyboardLayout, pub layout: KeyboardLayout,
} }
pub fn initialize_settings() { impl Default for KeyboardSettings {
SETTINGS.set(&KeyboardSettings { fn default() -> Self {
layout: KeyboardLayout::Qwerty, Self {
}); layout: KeyboardLayout::Qwerty,
register_nvim_setting!("keyboard_layout", KeyboardSettings::layout); }
}
} }

@ -1,7 +1,8 @@
use super::keyboard::initialize_settings as keyboard_initialize_settings;
use crate::settings::*; use crate::settings::*;
#[derive(Clone)] pub use super::keyboard::KeyboardSettings;
#[derive(Clone, SettingGroup)]
pub struct WindowSettings { pub struct WindowSettings {
pub refresh_rate: u64, pub refresh_rate: u64,
pub transparency: f32, pub transparency: f32,
@ -9,22 +10,15 @@ pub struct WindowSettings {
pub fullscreen: bool, pub fullscreen: bool,
} }
pub fn initialize_settings() { impl Default for WindowSettings {
let no_idle = SETTINGS fn default() -> Self {
.neovim_arguments Self {
.contains(&String::from("--noIdle")); refresh_rate: 60,
transparency: 1.0,
SETTINGS.set(&WindowSettings { no_idle: SETTINGS
refresh_rate: 60, .neovim_arguments
transparency: 1.0, .contains(&String::from("--noIdle")),
no_idle, fullscreen: false,
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();
} }

Loading…
Cancel
Save