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

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

@ -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,13 +1,14 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
#[macro_use]
mod settings;
extern crate neovide_derive;
mod bridge;
mod editor;
mod error_handling;
mod redraw_scheduler;
mod renderer;
mod settings;
mod window;
#[macro_use]
@ -17,18 +18,17 @@ 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 std::{
process,
sync::{atomic::AtomicBool, mpsc::channel, Arc},
};
use crossfire::mpsc::unbounded_future;
use window::window_geometry;
use bridge::start_bridge;
use editor::start_editor;
use window::create_window;
use renderer::{cursor_renderer::CursorSettings, RendererSettings};
use window::{create_window, window_geometry, KeyboardSettings, WindowSettings};
pub const INITIAL_DIMENSIONS: (u64, u64) = (100, 50);
@ -139,10 +139,11 @@ fn main() {
}
}
window::initialize_settings();
redraw_scheduler::initialize_settings();
renderer::initialize_settings();
renderer::cursor_renderer::initialize_settings();
KeyboardSettings::register();
WindowSettings::register();
redraw_scheduler::RedrawSettings::register();
RendererSettings::register();
CursorSettings::register();
let running = Arc::new(AtomicBool::new(true));

@ -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
impl Default for RedrawSettings {
fn default() -> Self {
Self {
extra_buffer_frames: if SETTINGS
.neovim_arguments
.contains(&String::from("--extraBufferFrames"))
.contains(&"--extraBufferFrames".to_string())
{
60
} else {
1
};
SETTINGS.set(&RedrawSettings {
extra_buffer_frames: buffer_frames,
});
register_nvim_setting!("extra_buffer_frames", RedrawSettings::extra_buffer_frames);
},
}
}
}
pub struct RedrawScheduler {

@ -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,8 +36,9 @@ pub struct CursorSettings {
vfx_particle_curl: f32,
}
pub fn initialize_settings() {
SETTINGS.set(&CursorSettings {
impl Default for CursorSettings {
fn default() -> Self {
CursorSettings {
antialiasing: true,
animation_length: 0.13,
animate_in_insert_mode: true,
@ -49,41 +50,10 @@ pub fn initialize_settings() {
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
);
}
}
}
// ----------------------------------------------------------------------------
#[derive(Debug, Clone)]
pub struct Corner {
start_position: Point,

@ -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 {
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,
});
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 {
rendered_windows: HashMap<u64, RenderedWindow>,
cursor_renderer: CursorRenderer,
@ -90,7 +75,6 @@ impl Renderer {
Renderer {
rendered_windows,
cursor_renderer,
current_mode,
paint,
shaper,

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

@ -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<T> = fn(T, bool) -> Option<Token<'static>>;

@ -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 {
impl Default for KeyboardSettings {
fn default() -> Self {
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::*;
#[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 {
impl Default for WindowSettings {
fn default() -> Self {
Self {
refresh_rate: 60,
transparency: 1.0,
no_idle,
no_idle: SETTINGS
.neovim_arguments
.contains(&String::from("--noIdle")),
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