diff --git a/Cargo.lock b/Cargo.lock index fb9f62b..bdceb08 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -360,7 +360,7 @@ dependencies = [ [[package]] name = "swayr" -version = "0.8.0" +version = "0.9.0-beta.0" dependencies = [ "clap", "directories", diff --git a/Cargo.toml b/Cargo.toml index 0755aba..5473ef6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "swayr" -version = "0.8.0" +version = "0.9.0-beta.0" description = "A LRU window-switcher (and more) for the sway window manager" homepage = "https://sr.ht/~tsdh/swayr/" repository = "https://git.sr.ht/~tsdh/swayr" diff --git a/NEWS.md b/NEWS.md index 8f58ff1..8979457 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,14 @@ +swayr v0.9.0 +============ + +- All menu switching commands (`switch-window`, `switch-workspace`, and + `switch-workspace-or-window`) can now create and switch to not yet existing + workspaces. Just type a digit, a name, or `:`, confirm your + input, and it'll do. The `:` format is explained in `man + sway(5)`. If that format is given, `swayr` will create the workspace using + `workspace number :`. If just a digit or name is given, the + `number` argument is not used. + swayr v0.8.0 ============ diff --git a/src/cmds.rs b/src/cmds.rs index 3318413..b596a78 100644 --- a/src/cmds.rs +++ b/src/cmds.rs @@ -21,6 +21,7 @@ use crate::config as cfg; use crate::layout; use crate::util; use crate::util::DisplayFormat; +use lazy_static::lazy_static; use rand::prelude::SliceRandom; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -349,8 +350,7 @@ pub fn exec_swayr_cmd(args: ExecSwayrCmdArgs) { }) } - if let Some(c) = - util::select_from_menu("Select swayr command", &cmds) + if let Ok(c) = util::select_from_menu("Select swayr command", &cmds) { exec_swayr_cmd(ExecSwayrCmdArgs { cmd: c, @@ -393,12 +393,26 @@ pub fn switch_to_urgent_or_lru_window( } } +lazy_static! { + static ref DIGIT_AND_NAME: regex::Regex = + regex::Regex::new(r"(\d):(.*)").unwrap(); +} + +pub fn create_workspace(ws_name: &str) { + if DIGIT_AND_NAME.is_match(ws_name) { + run_sway_command(&["workspace", "number", ws_name]); + } else { + run_sway_command(&["workspace", ws_name]); + } +} + pub fn switch_window(extra_props: &HashMap) { let root = get_tree(); let windows = con::get_windows(&root, true, extra_props); - if let Some(window) = con::select_window("Switch to window", &windows) { - focus_window_by_id(window.get_id()) + match util::select_from_menu("Switch to window", &windows) { + Ok(window) => focus_window_by_id(window.get_id()), + Err(ws_name) => create_workspace(&ws_name), } } @@ -507,10 +521,9 @@ pub fn switch_workspace(extra_props: &HashMap) { let root = get_tree(); let workspaces = con::get_workspaces(&root, false, extra_props); - if let Some(workspace) = - con::select_workspace("Switch to workspace", &workspaces) - { - run_sway_command(&["workspace", "number", workspace.get_name()]); + match util::select_from_menu("Switch to workspace", &workspaces) { + Ok(workspace) => run_sway_command(&["workspace", workspace.get_name()]), + Err(ws_name) => create_workspace(&ws_name), } } @@ -518,16 +531,14 @@ pub fn switch_workspace_or_window(extra_props: &HashMap) { let root = get_tree(); let workspaces = con::get_workspaces(&root, true, extra_props); let ws_or_wins = con::WsOrWin::from_workspaces(&workspaces); - if let Some(ws_or_win) = con::select_workspace_or_window( - "Select workspace or window", - &ws_or_wins, - ) { - match ws_or_win { + match util::select_from_menu("Select workspace or window", &ws_or_wins) { + Ok(ws_or_win) => match ws_or_win { con::WsOrWin::Ws { ws } => { - run_sway_command(&["workspace", "number", ws.get_name()]); + run_sway_command(&["workspace", ws.get_name()]); } con::WsOrWin::Win { win } => focus_window_by_id(win.get_id()), - } + }, + Err(ws_name) => create_workspace(&ws_name), } } @@ -535,7 +546,7 @@ pub fn quit_window(extra_props: &HashMap) { let root = get_tree(); let windows = con::get_windows(&root, true, extra_props); - if let Some(window) = con::select_window("Quit window", &windows) { + if let Ok(window) = util::select_from_menu("Quit window", &windows) { quit_window_by_id(window.get_id()) } } @@ -544,8 +555,8 @@ pub fn quit_workspace_or_window(extra_props: &HashMap) { let root = get_tree(); let workspaces = con::get_workspaces(&root, true, extra_props); let ws_or_wins = con::WsOrWin::from_workspaces(&workspaces); - if let Some(ws_or_win) = - con::select_workspace_or_window("Quit workspace or window", &ws_or_wins) + if let Ok(ws_or_win) = + util::select_from_menu("Quit workspace or window", &ws_or_wins) { match ws_or_win { con::WsOrWin::Ws { ws } => { @@ -758,8 +769,14 @@ impl DisplayFormat for SwaymsgCmd<'_> { pub fn exec_swaymsg_command() { let cmds = get_swaymsg_commands(); let cmd = util::select_from_menu("Execute swaymsg command", &cmds); - if let Some(cmd) = cmd { - run_sway_command(&cmd.cmd); + match cmd { + Ok(cmd) => run_sway_command(&cmd.cmd), + Err(cmd) if !cmd.is_empty() => { + run_sway_command( + &cmd.split_ascii_whitespace().collect::>(), + ); + } + Err(_) => (), } } diff --git a/src/con.rs b/src/con.rs index 742adb5..8382f17 100644 --- a/src/con.rs +++ b/src/con.rs @@ -397,20 +397,6 @@ pub fn get_workspaces<'a>( workspaces } -pub fn select_window<'a>( - prompt: &str, - windows: &'a [Window], -) -> Option<&'a Window<'a>> { - util::select_from_menu(prompt, windows) -} - -pub fn select_workspace<'a>( - prompt: &str, - workspaces: &'a [Workspace], -) -> Option<&'a Workspace<'a>> { - util::select_from_menu(prompt, workspaces) -} - pub enum WsOrWin<'a> { Ws { ws: &'a Workspace<'a> }, Win { win: &'a Window<'a> }, @@ -440,13 +426,6 @@ impl WsOrWin<'_> { } } -pub fn select_workspace_or_window<'a>( - prompt: &'a str, - ws_or_wins: &'a [WsOrWin<'a>], -) -> Option<&'a WsOrWin<'a>> { - util::select_from_menu(prompt, ws_or_wins) -} - pub struct Workspace<'a> { pub node: &'a s::Node, extra_props: Option, diff --git a/src/util.rs b/src/util.rs index 040cc62..a450e4d 100644 --- a/src/util.rs +++ b/src/util.rs @@ -122,7 +122,7 @@ fn find_icon(icon_name: &str, icon_dirs: &[String]) -> Option> { lazy_static! { static ref WM_CLASS_OR_ICON_RX: regex::Regex = - regex::Regex::new("(StartupWMClass|Icon)=(.+)").unwrap(); + regex::Regex::new(r"(StartupWMClass|Icon)=(.+)").unwrap(); static ref REV_DOMAIN_NAME_RX: regex::Regex = regex::Regex::new(r"^(?:[a-zA-Z0-9-]+\.)+([a-zA-Z0-9-]+)$").unwrap(); } @@ -234,7 +234,7 @@ pub trait DisplayFormat { pub fn select_from_menu<'a, 'b, TS>( prompt: &'a str, choices: &'b [TS], -) -> Option<&'b TS> +) -> Result<&'b TS, String> where TS: DisplayFormat + Sized, { @@ -287,5 +287,5 @@ where let choice = String::from_utf8_lossy(&output.stdout); let mut choice = String::from(choice); choice.pop(); // Remove trailing \n from choice. - map.get(&choice).copied() + map.get(&choice).copied().ok_or(choice) }