mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-22 02:01:55 +07:00
Partially implement config includes
Subsequent commits will add merging for all leftover sections.
This commit is contained in:
@@ -1103,8 +1103,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rule_color_can_override_base_gradient() {
|
fn rule_color_can_override_base_gradient() {
|
||||||
let config = Config::parse(
|
let config = Config::parse_mem(
|
||||||
"test.kdl",
|
|
||||||
r##"
|
r##"
|
||||||
// Start with gradient set.
|
// Start with gradient set.
|
||||||
layout {
|
layout {
|
||||||
@@ -1127,7 +1126,7 @@ mod tests {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut border = config.resolve_layout().border;
|
let mut border = config.layout.border;
|
||||||
for rule in &config.window_rules {
|
for rule in &config.window_rules {
|
||||||
border.merge_with(&rule.border);
|
border.merge_with(&rule.border);
|
||||||
}
|
}
|
||||||
@@ -1151,8 +1150,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rule_color_can_override_rule_gradient() {
|
fn rule_color_can_override_rule_gradient() {
|
||||||
let config = Config::parse(
|
let config = Config::parse_mem(
|
||||||
"test.kdl",
|
|
||||||
r##"
|
r##"
|
||||||
// Start with gradient set.
|
// Start with gradient set.
|
||||||
layout {
|
layout {
|
||||||
@@ -1196,7 +1194,7 @@ mod tests {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut border = config.resolve_layout().border;
|
let mut border = config.layout.border;
|
||||||
let mut tab_indicator_rule = TabIndicatorRule::default();
|
let mut tab_indicator_rule = TabIndicatorRule::default();
|
||||||
for rule in &config.window_rules {
|
for rule in &config.window_rules {
|
||||||
border.merge_with(&rule.border);
|
border.merge_with(&rule.border);
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
use std::error::Error;
|
||||||
|
use std::fmt;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use miette::Diagnostic;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ConfigParseResult<T, E> {
|
||||||
|
pub config: Result<T, E>,
|
||||||
|
|
||||||
|
// We always try to return includes for the file watcher.
|
||||||
|
//
|
||||||
|
// If the main config is valid, but an included file fails to parse, config will be an Err(),
|
||||||
|
// but includes will still be filled, so that fixing just the included file is enough to
|
||||||
|
// trigger a reload.
|
||||||
|
pub includes: Vec<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error type that chains main errors with include errors.
|
||||||
|
///
|
||||||
|
/// Allows miette's Report formatting to have main + include errors all in one.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ConfigIncludeError {
|
||||||
|
pub main: knuffel::Error,
|
||||||
|
pub includes: Vec<knuffel::Error>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, E> ConfigParseResult<T, E> {
|
||||||
|
pub fn from_err(err: E) -> Self {
|
||||||
|
Self {
|
||||||
|
config: Err(err),
|
||||||
|
includes: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_config_res<U, V>(
|
||||||
|
self,
|
||||||
|
f: impl FnOnce(Result<T, E>) -> Result<U, V>,
|
||||||
|
) -> ConfigParseResult<U, V> {
|
||||||
|
ConfigParseResult {
|
||||||
|
config: f(self.config),
|
||||||
|
includes: self.includes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ConfigIncludeError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt::Display::fmt(&self.main, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for ConfigIncludeError {
|
||||||
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
|
self.main.source()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Diagnostic for ConfigIncludeError {
|
||||||
|
fn code<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
|
||||||
|
self.main.code()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn severity(&self) -> Option<miette::Severity> {
|
||||||
|
self.main.severity()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn help<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
|
||||||
|
self.main.help()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn url<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
|
||||||
|
self.main.url()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn source_code(&self) -> Option<&dyn miette::SourceCode> {
|
||||||
|
self.main.source_code()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
|
||||||
|
self.main.labels()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
|
||||||
|
self.main.diagnostic_source()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
|
||||||
|
let main_related = self.main.related();
|
||||||
|
let includes_iter = self.includes.iter().map(|err| err as &'a dyn Diagnostic);
|
||||||
|
|
||||||
|
let iter: Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a> = match main_related {
|
||||||
|
Some(main) => Box::new(main.chain(includes_iter)),
|
||||||
|
None => Box::new(includes_iter),
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(iter)
|
||||||
|
}
|
||||||
|
}
|
||||||
+555
-328
File diff suppressed because it is too large
Load Diff
@@ -36,6 +36,17 @@ impl Default for Cursor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(knuffel::Decode, Debug, Clone, PartialEq)]
|
||||||
|
pub struct ScreenshotPath(#[knuffel(argument)] pub Option<String>);
|
||||||
|
|
||||||
|
impl Default for ScreenshotPath {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(Some(String::from(
|
||||||
|
"~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png",
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq, Eq)]
|
#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct HotkeyOverlay {
|
pub struct HotkeyOverlay {
|
||||||
#[knuffel(child)]
|
#[knuffel(child)]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
struct KdlCodeBlock {
|
struct KdlCodeBlock {
|
||||||
filename: String,
|
filename: String,
|
||||||
@@ -84,7 +84,7 @@ fn wiki_docs_parses() {
|
|||||||
must_fail,
|
must_fail,
|
||||||
} in code_blocks
|
} in code_blocks
|
||||||
{
|
{
|
||||||
if let Err(error) = niri_config::Config::parse(&filename, &code) {
|
if let Err(error) = niri_config::Config::parse(Path::new(&filename), &code).config {
|
||||||
if !must_fail {
|
if !must_fail {
|
||||||
errors.push(format!(
|
errors.push(format!(
|
||||||
"Error parsing wiki KDL code block at {}:{}: {:?}",
|
"Error parsing wiki KDL code block at {}:{}: {:?}",
|
||||||
|
|||||||
+2
-2
@@ -59,7 +59,7 @@ impl MappedLayer {
|
|||||||
clock: Clock,
|
clock: Clock,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut shadow_config = config.resolve_layout().shadow;
|
let mut shadow_config = config.layout.shadow;
|
||||||
// Shadows for layer surfaces need to be explicitly enabled.
|
// Shadows for layer surfaces need to be explicitly enabled.
|
||||||
shadow_config.on = false;
|
shadow_config.on = false;
|
||||||
shadow_config.merge_with(&rules.shadow);
|
shadow_config.merge_with(&rules.shadow);
|
||||||
@@ -76,7 +76,7 @@ impl MappedLayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_config(&mut self, config: &Config) {
|
pub fn update_config(&mut self, config: &Config) {
|
||||||
let mut shadow_config = config.resolve_layout().shadow;
|
let mut shadow_config = config.layout.shadow;
|
||||||
// Shadows for layer surfaces need to be explicitly enabled.
|
// Shadows for layer surfaces need to be explicitly enabled.
|
||||||
shadow_config.on = false;
|
shadow_config.on = false;
|
||||||
shadow_config.merge_with(&self.rules.shadow);
|
shadow_config.merge_with(&self.rules.shadow);
|
||||||
|
|||||||
+1
-1
@@ -576,7 +576,7 @@ impl HitType {
|
|||||||
impl Options {
|
impl Options {
|
||||||
fn from_config(config: &Config) -> Self {
|
fn from_config(config: &Config) -> Self {
|
||||||
Self {
|
Self {
|
||||||
layout: config.resolve_layout(),
|
layout: config.layout.clone(),
|
||||||
animations: config.animations.clone(),
|
animations: config.animations.clone(),
|
||||||
gestures: config.gestures,
|
gestures: config.gestures,
|
||||||
overview: config.overview,
|
overview: config.overview,
|
||||||
|
|||||||
+5
-5
@@ -2357,9 +2357,9 @@ fn removing_all_outputs_preserves_empty_named_workspaces() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn config_change_updates_cached_sizes() {
|
fn config_change_updates_cached_sizes() {
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
let border = config.layout.border.get_or_insert_with(Default::default);
|
let border = &mut config.layout.border;
|
||||||
border.off = false;
|
border.off = false;
|
||||||
border.width = Some(FloatOrInt(2.));
|
border.width = 2.;
|
||||||
|
|
||||||
let mut layout = Layout::new(Clock::default(), &config);
|
let mut layout = Layout::new(Clock::default(), &config);
|
||||||
|
|
||||||
@@ -2371,7 +2371,7 @@ fn config_change_updates_cached_sizes() {
|
|||||||
}
|
}
|
||||||
.apply(&mut layout);
|
.apply(&mut layout);
|
||||||
|
|
||||||
config.layout.border.as_mut().unwrap().width = Some(FloatOrInt(4.));
|
config.layout.border.width = 4.;
|
||||||
layout.update_config(&config);
|
layout.update_config(&config);
|
||||||
|
|
||||||
layout.verify_invariants();
|
layout.verify_invariants();
|
||||||
@@ -2380,7 +2380,7 @@ fn config_change_updates_cached_sizes() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn preset_height_change_removes_preset() {
|
fn preset_height_change_removes_preset() {
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
config.layout.preset_window_heights = Some(vec![PresetSize::Fixed(1), PresetSize::Fixed(2)]);
|
config.layout.preset_window_heights = vec![PresetSize::Fixed(1), PresetSize::Fixed(2)];
|
||||||
|
|
||||||
let mut layout = Layout::new(Clock::default(), &config);
|
let mut layout = Layout::new(Clock::default(), &config);
|
||||||
|
|
||||||
@@ -2401,7 +2401,7 @@ fn preset_height_change_removes_preset() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Leave only one.
|
// Leave only one.
|
||||||
config.layout.preset_window_heights = Some(vec![PresetSize::Fixed(1)]);
|
config.layout.preset_window_heights = vec![PresetSize::Fixed(1)];
|
||||||
|
|
||||||
layout.update_config(&config);
|
layout.update_config(&config);
|
||||||
|
|
||||||
|
|||||||
+7
-6
@@ -24,7 +24,7 @@ use niri::utils::spawning::{
|
|||||||
REMOVE_ENV_RUST_BACKTRACE, REMOVE_ENV_RUST_LIB_BACKTRACE,
|
REMOVE_ENV_RUST_BACKTRACE, REMOVE_ENV_RUST_LIB_BACKTRACE,
|
||||||
};
|
};
|
||||||
use niri::utils::{cause_panic, version, watcher, xwayland, IS_SYSTEMD_SERVICE};
|
use niri::utils::{cause_panic, version, watcher, xwayland, IS_SYSTEMD_SERVICE};
|
||||||
use niri_config::ConfigPath;
|
use niri_config::{Config, ConfigPath};
|
||||||
use niri_ipc::socket::SOCKET_PATH_ENV;
|
use niri_ipc::socket::SOCKET_PATH_ENV;
|
||||||
use portable_atomic::Ordering;
|
use portable_atomic::Ordering;
|
||||||
use sd_notify::NotifyState;
|
use sd_notify::NotifyState;
|
||||||
@@ -101,7 +101,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
Sub::Validate { config } => {
|
Sub::Validate { config } => {
|
||||||
tracy_client::Client::start();
|
tracy_client::Client::start();
|
||||||
|
|
||||||
config_path(config).load()?;
|
config_path(config).load().config?;
|
||||||
info!("config is valid");
|
info!("config is valid");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -148,10 +148,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
let config_path = config_path(cli.config);
|
let config_path = config_path(cli.config);
|
||||||
env::remove_var("NIRI_CONFIG");
|
env::remove_var("NIRI_CONFIG");
|
||||||
let (config_created_at, config_load_result) = config_path.load_or_create();
|
let (config_created_at, config_load_result) = config_path.load_or_create();
|
||||||
let config_errored = config_load_result.is_err();
|
let config_errored = config_load_result.config.is_err();
|
||||||
let mut config = config_load_result
|
let mut config = config_load_result.config.unwrap_or_else(|err| {
|
||||||
.map_err(|err| warn!("{err:?}"))
|
warn!("{err:?}");
|
||||||
.unwrap_or_default();
|
Config::load_default()
|
||||||
|
});
|
||||||
|
|
||||||
let spawn_at_startup = mem::take(&mut config.spawn_at_startup);
|
let spawn_at_startup = mem::take(&mut config.spawn_at_startup);
|
||||||
let spawn_sh_at_startup = mem::take(&mut config.spawn_sh_at_startup);
|
let spawn_sh_at_startup = mem::take(&mut config.spawn_sh_at_startup);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::time::Duration;
|
|||||||
|
|
||||||
use insta::assert_snapshot;
|
use insta::assert_snapshot;
|
||||||
use niri_config::animations::{Curve, EasingParams, Kind};
|
use niri_config::animations::{Curve, EasingParams, Kind};
|
||||||
use niri_config::{Config, FloatOrInt};
|
use niri_config::Config;
|
||||||
use niri_ipc::SizeChange;
|
use niri_ipc::SizeChange;
|
||||||
use smithay::utils::{Point, Size};
|
use smithay::utils::{Point, Size};
|
||||||
use wayland_client::protocol::wl_surface::WlSurface;
|
use wayland_client::protocol::wl_surface::WlSurface;
|
||||||
@@ -74,7 +74,7 @@ fn set_up() -> Fixture {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
config.layout.gaps = Some(FloatOrInt(0.0));
|
config.layout.gaps = 0.0;
|
||||||
config.animations.window_resize.anim.kind = LINEAR;
|
config.animations.window_resize.anim.kind = LINEAR;
|
||||||
config.animations.window_movement.0.kind = LINEAR;
|
config.animations.window_movement.0.kind = LINEAR;
|
||||||
|
|
||||||
|
|||||||
@@ -795,7 +795,7 @@ window-rule {
|
|||||||
max-width 300
|
max-width 300
|
||||||
}
|
}
|
||||||
"##;
|
"##;
|
||||||
let config = Config::parse("test.kdl", config).unwrap();
|
let config = Config::parse_mem(config).unwrap();
|
||||||
let mut f = Fixture::with_config(config);
|
let mut f = Fixture::with_config(config);
|
||||||
f.add_output(1, (1920, 1080));
|
f.add_output(1, (1920, 1080));
|
||||||
f.add_output(2, (1280, 720));
|
f.add_output(2, (1280, 720));
|
||||||
|
|||||||
@@ -281,7 +281,7 @@ window-rule {{
|
|||||||
|
|
||||||
snapshot_desc.push(format!("config:{config}"));
|
snapshot_desc.push(format!("config:{config}"));
|
||||||
|
|
||||||
let config = Config::parse("config.kdl", &config).unwrap();
|
let config = Config::parse_mem(&config).unwrap();
|
||||||
|
|
||||||
let mut f = Fixture::with_config(config);
|
let mut f = Fixture::with_config(config);
|
||||||
f.add_output(1, (1280, 720));
|
f.add_output(1, (1280, 720));
|
||||||
@@ -574,7 +574,7 @@ layout {
|
|||||||
|
|
||||||
snapshot_desc.push(format!("config:{config}"));
|
snapshot_desc.push(format!("config:{config}"));
|
||||||
|
|
||||||
let config = Config::parse("config.kdl", &config).unwrap();
|
let config = Config::parse_mem(&config).unwrap();
|
||||||
|
|
||||||
let mut f = Fixture::with_config(config);
|
let mut f = Fixture::with_config(config);
|
||||||
f.add_output(1, (1280, 720));
|
f.add_output(1, (1280, 720));
|
||||||
|
|||||||
@@ -607,7 +607,7 @@ mod tests {
|
|||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn check(config: &str, action: Action) -> String {
|
fn check(config: &str, action: Action) -> String {
|
||||||
let config = Config::parse("test.kdl", config).unwrap();
|
let config = Config::parse_mem(config).unwrap();
|
||||||
if let Some((key, title)) = format_bind(&config.binds.0, &action) {
|
if let Some((key, title)) = format_bind(&config.binds.0, &action) {
|
||||||
let key = key.map(|key| key_name(false, ModKey::Super, &key));
|
let key = key.map(|key| key_name(false, ModKey::Super, &key));
|
||||||
let key = key.as_deref().unwrap_or("(not bound)");
|
let key = key.as_deref().unwrap_or("(not bound)");
|
||||||
|
|||||||
+1
-1
@@ -213,7 +213,7 @@ pub fn expand_home(path: &Path) -> anyhow::Result<Option<PathBuf>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_screenshot_path(config: &Config) -> anyhow::Result<Option<PathBuf>> {
|
pub fn make_screenshot_path(config: &Config) -> anyhow::Result<Option<PathBuf>> {
|
||||||
let Some(path) = &config.screenshot_path else {
|
let Some(path) = &config.screenshot_path.0 else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ pub fn setup(state: &mut State, config_path: &ConfigPath) {
|
|||||||
// Parsing the config actually takes > 20 ms on my beefy machine, so let's do it on the
|
// Parsing the config actually takes > 20 ms on my beefy machine, so let's do it on the
|
||||||
// watcher thread.
|
// watcher thread.
|
||||||
let process = |path: &ConfigPath| {
|
let process = |path: &ConfigPath| {
|
||||||
path.load().map_err(|err| {
|
path.load().config.map_err(|err| {
|
||||||
warn!("{err:?}");
|
warn!("{err:?}");
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user