From 09a24b89e540f6f228c9031a8bd26cbc028807de Mon Sep 17 00:00:00 2001 From: LGUG2Z Date: Fri, 1 Apr 2022 18:25:02 -0700 Subject: [PATCH] feat(config): add cfgen for apps based on yaml def This commit introduces a configuration generator for application-specific config options passed to the cli via a file path. The hope is to have a public repository that any user can contribute application-specific configs and fixes to, and for the generated AHK to be available to any new user as part of the initial setup to make the onboarding as frictionless as possible. re #62 --- Cargo.lock | 17 ++-- komorebi-core/src/config_generation.rs | 119 +++++++++++++++++++++++++ komorebi-core/src/lib.rs | 2 + komorebic.lib.sample.ahk | 4 + komorebic/Cargo.toml | 1 + komorebic/src/main.rs | 39 ++++++++ 6 files changed, 174 insertions(+), 8 deletions(-) create mode 100644 komorebi-core/src/config_generation.rs diff --git a/Cargo.lock b/Cargo.lock index 63326c9b..cb136817 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -84,9 +84,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "3.1.6" +version = "3.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8c93436c21e4698bacadf42917db28b23017027a4deccb35dbe47a7e7840123" +checksum = "71c47df61d9e16dc010b55dba1952a57d8c215dbb533fd13cdd13369aac73b1c" dependencies = [ "atty", "bitflags", @@ -102,9 +102,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.1.4" +version = "3.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95d038ede1a964ce99f49cbe27a7fb538d1da595e4b4f70b8c8f338d17bf16" +checksum = "a3aab4734e083b809aaf5794e14e756d1c798d2c69c7f7de7a09a2f5214993c1" dependencies = [ "heck", "proc-macro-error", @@ -505,6 +505,7 @@ dependencies = [ "powershell_script", "serde", "serde_json", + "serde_yaml", "uds_windows", "windows", ] @@ -1305,9 +1306,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa31669fa42c09c34d94d8165dd2012e8ff3c66aca50f3bb226b68f216f2706c" +checksum = "90442985ee2f57c9e1b548ee72ae842f4a9a20e3f417cc38dbc5dc684d9bb4ee" dependencies = [ "lazy_static", "valuable", @@ -1336,9 +1337,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e0ab7bdc962035a87fba73f3acca9b8a8d0034c2e6f60b84aeaaddddc155dce" +checksum = "b9df98b037d039d03400d9dd06b0f8ce05486b5f25e9a2d7d36196e142ebbc52" dependencies = [ "ansi_term", "lazy_static", diff --git a/komorebi-core/src/config_generation.rs b/komorebi-core/src/config_generation.rs new file mode 100644 index 00000000..8233f66e --- /dev/null +++ b/komorebi-core/src/config_generation.rs @@ -0,0 +1,119 @@ +use clap::ArgEnum; +use color_eyre::Result; +use schemars::JsonSchema; +use serde::Deserialize; +use serde::Serialize; +use strum::Display; +use strum::EnumString; + +use crate::ApplicationIdentifier; + +#[derive(Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)] +#[strum(serialize_all = "snake_case")] +#[serde(rename_all = "snake_case")] +pub enum ApplicationOptions { + ObjectNameChange, + Layered, + BorderOverflow, + TrayAndMultiWindow, + Force, +} + +impl ApplicationOptions { + #[must_use] + pub fn cfgen(&self, kind: &ApplicationIdentifier, id: &str) -> String { + format!( + "Run, {}, , Hide", + match self { + ApplicationOptions::ObjectNameChange => { + format!( + "komorebic.exe identify-object-name-change-application {} {}", + kind, id + ) + } + ApplicationOptions::Layered => { + format!("komorebic.exe identify-layered-application {} {}", kind, id) + } + ApplicationOptions::BorderOverflow => { + format!( + "komorebic.exe identify-border-overflow-application {} {}", + kind, id + ) + } + ApplicationOptions::TrayAndMultiWindow => { + format!("komorebic.exe identify-tray-application {} {}", kind, id) + } + ApplicationOptions::Force => { + format!("komorebic.exe manage-rule {} {}", kind, id) + } + } + ) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +pub struct IdWithIdentifier { + kind: ApplicationIdentifier, + id: String, +} + +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +pub struct ApplicationConfiguration { + name: String, + identifier: IdWithIdentifier, + options: Option>, + float_identifiers: Option>, +} + +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +pub struct ApplicationConfigurationGenerator; + +impl ApplicationConfigurationGenerator { + fn load(content: &str) -> Result> { + Ok(serde_yaml::from_str(content)?) + } + + pub fn generate(content: &str) -> Result> { + let mut cfgen = Self::load(content)?; + cfgen.sort_by(|a, b| a.name.cmp(&b.name)); + + let mut lines = vec![ + String::from("; Generated by komorebic.exe"), + String::from("; To use this file, add the line below to the top of your komorebi.ahk configuration file"), + String::from("; #Include %A_ScriptDir%\\komorebi.generated.ahk"), + String::from("") + ]; + + let mut float_rules = vec![]; + + for app in cfgen { + lines.push(format!("; {}", app.name)); + if let Some(options) = app.options { + for opt in options { + if let ApplicationOptions::TrayAndMultiWindow = opt { + lines.push(String::from("; If you have disabled minimize/close to tray for this application, you can delete/comment out the next line")); + } + lines.push(opt.cfgen(&app.identifier.kind, &app.identifier.id)); + } + } + + if let Some(float_identifiers) = app.float_identifiers { + for float in float_identifiers { + let float_rule = format!( + "Run, komorebic.exe float-rule {}, {}, , Hide", + float.kind, float.id + ); + + // Don't want to send duped signals especially as configs get larger + if !float_rules.contains(&float_rule) { + float_rules.push(float_rule.clone()); + lines.push(float_rule); + } + } + } + lines.push(String::from("")); + } + + Ok(lines) + } +} diff --git a/komorebi-core/src/lib.rs b/komorebi-core/src/lib.rs index 085d3cd2..2ed4cbf9 100644 --- a/komorebi-core/src/lib.rs +++ b/komorebi-core/src/lib.rs @@ -23,6 +23,7 @@ pub use operation_direction::OperationDirection; pub use rect::Rect; pub mod arrangement; +pub mod config_generation; pub mod custom_layout; pub mod cycle_direction; pub mod default_layout; @@ -138,6 +139,7 @@ pub enum StateQuery { #[derive(Clone, Debug, Serialize, Deserialize, Display, EnumString, ArgEnum, JsonSchema)] #[strum(serialize_all = "snake_case")] +#[serde(rename_all = "snake_case")] pub enum ApplicationIdentifier { Exe, Class, diff --git a/komorebic.lib.sample.ahk b/komorebic.lib.sample.ahk index 77838a16..2f3a437c 100644 --- a/komorebic.lib.sample.ahk +++ b/komorebic.lib.sample.ahk @@ -304,6 +304,10 @@ AhkLibrary() { Run, komorebic.exe ahk-library, , Hide } +ApplicationSpecificConfiguration(path) { + Run, komorebic.exe application-specific-configuration %path%, , Hide +} + NotificationSchema() { Run, komorebic.exe notification-schema, , Hide } \ No newline at end of file diff --git a/komorebic/Cargo.toml b/komorebic/Cargo.toml index 054ed2bc..d479441f 100644 --- a/komorebic/Cargo.toml +++ b/komorebic/Cargo.toml @@ -24,6 +24,7 @@ paste = "1" powershell_script = "0.3" serde = { version = "1", features = ["derive"] } serde_json = "1" +serde_yaml = "0.8" uds_windows = "1" [dependencies.windows] diff --git a/komorebic/src/main.rs b/komorebic/src/main.rs index 61e329e8..50b29fd7 100644 --- a/komorebic/src/main.rs +++ b/komorebic/src/main.rs @@ -1,6 +1,7 @@ #![warn(clippy::all, clippy::nursery, clippy::pedantic)] #![allow(clippy::missing_errors_doc)] +use std::fs; use std::fs::File; use std::fs::OpenOptions; use std::io::BufRead; @@ -28,6 +29,7 @@ use windows::Win32::UI::WindowsAndMessaging::SW_RESTORE; use derive_ahk::AhkFunction; use derive_ahk::AhkLibrary; +use komorebi_core::config_generation::ApplicationConfigurationGenerator; use komorebi_core::ApplicationIdentifier; use komorebi_core::Axis; use komorebi_core::CycleDirection; @@ -420,6 +422,12 @@ struct Unsubscribe { named_pipe: String, } +#[derive(Parser, AhkFunction)] +pub struct ApplicationSpecificConfiguration { + /// YAML file from which the application-specific configurations should be loaded + path: String, +} + #[derive(Parser)] #[clap(author, about, version, setting = AppSettings::DeriveDisplayOrder)] struct Opts { @@ -641,6 +649,10 @@ enum SubCommand { ToggleMouseFollowsFocus, /// Generate a library of AutoHotKey helper functions AhkLibrary, + /// Generate a collection of common application configurations to use in komorebi.ahk + #[clap(arg_required_else_help = true)] + #[clap(alias = "app-specific-configuration")] + ApplicationSpecificConfiguration(ApplicationSpecificConfiguration), /// Generate a JSON Schema of subscription notifications NotificationSchema, } @@ -1135,6 +1147,33 @@ fn main() -> Result<()> { SubCommand::WindowHidingBehaviour(arg) => { send_message(&*SocketMessage::WindowHidingBehaviour(arg.hiding_behaviour).as_bytes()?)?; } + SubCommand::ApplicationSpecificConfiguration(arg) => { + let content = fs::read_to_string(resolve_windows_path(&arg.path)?)?; + let lines = ApplicationConfigurationGenerator::generate(&content)?; + + let mut generated_config = HOME_DIR.clone(); + generated_config.push("komorebi.generated.ahk"); + let mut file = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(generated_config.clone())?; + + file.write_all(lines.join("\n").as_bytes())?; + + println!( + "\nApplication-specific generated configuration written to {}", + generated_config.to_str().ok_or_else(|| anyhow!( + "could not find the path to the generated configuration file" + ))? + ); + + println!( + "\nYou can include the generated configuration at the top of your komorebi.ahk config with this line:" + ); + + println!("\n#Include %A_ScriptDir%\\komorebi.generated.ahk"); + } SubCommand::NotificationSchema => { let home = HOME_DIR.clone(); let mut socket = home;