Skip to content

Commit 7a1bd7f

Browse files
committed
Implement layouts and fix a bug
1 parent 60ce56e commit 7a1bd7f

File tree

6 files changed

+141
-44
lines changed

6 files changed

+141
-44
lines changed

src/cli.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use clap::{Parser, Subcommand};
2+
use std::path::PathBuf;
23

34
// TODO: Make a `cli`directory with multiple files
45

@@ -16,11 +17,15 @@ pub enum Commands {
1617
///
1718
/// This command will initialize your config directories.
1819
Init,
19-
/// Manage tmux sessions related to directories
20+
/// Manage directories in the context of muxmate and tmux
2021
///
2122
/// This command provides functionalities to interact with tmux sessions based on directories.
2223
#[command(alias = "dir", alias = "dirs", alias = "directories")]
2324
Directory(DirectoryCli),
25+
/// Manage templates in the context of muxmate and tmux
26+
///
27+
/// This command provides functionalities to interact with tmux sessions based on templates
28+
#[command(alias = "temp", alias = "templ")]
2429
Template(TemplateCli),
2530
}
2631

@@ -75,5 +80,19 @@ pub enum TemplateCommands {
7580

7681
#[derive(Parser, Debug)]
7782
pub struct StartTemplateArgs {
78-
pub name: String,
83+
pub template_name: String,
84+
85+
/// Start the session detached
86+
#[arg(short, long, default_value_t = false)]
87+
pub detached: bool,
88+
89+
/// The directory to start it in
90+
#[arg(long, alias = "dir")]
91+
pub directory: Option<PathBuf>,
92+
93+
/// Specify the name of the tmux session
94+
///
95+
/// Optionally provide a name for the session. If not provided, it will be either the name from the configuration or from the directory
96+
#[arg(short, long)]
97+
pub name: Option<String>,
7998
}

src/commands/directory.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::{
22
cli::{DirectoryCli, DirectoryCommands, ListArgs, StartDirectoryArgs},
33
directories::{self, Directory},
4-
helpers::{absolute_path, Exit},
4+
helpers::{absolute_path, dir_name, Exit},
55
tmux::{attach, session_exists},
66
widgets::{heading::Heading, table::fmt_table},
77
};
@@ -81,12 +81,7 @@ fn resolve_dir_path(cli_args: &StartDirectoryArgs) -> (String, PathBuf) {
8181
None => {
8282
let relative_path = PathBuf::from(&cli_args.directory);
8383
let path = absolute_path(&relative_path).exit(1, "The path could not be generated");
84-
let name = user_name.unwrap_or(
85-
path.file_name()
86-
.and_then(|os_string| os_string.to_str())
87-
.map(|str| str.to_string())
88-
.unwrap_or("".to_string()),
89-
);
84+
let name = user_name.unwrap_or(dir_name(&path));
9085

9186
(name, path)
9287
}

src/commands/template.rs

Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use crate::{
2+
apply_if,
23
cli::{ListArgs, StartTemplateArgs, TemplateCli, TemplateCommands},
3-
helpers::Exit,
4-
templates::{parse_template_config, Template},
4+
helpers::{absolute_path, dir_name, Exit},
5+
templates::{apply_template, parse_template_config},
56
tmux::{attach, session_exists},
67
widgets::{heading::Heading, table::fmt_table},
78
};
9+
use std::path::PathBuf;
810
use tmux_interface::{NewSession, Tmux, TmuxCommand};
911

1012
pub fn template_handler(args: TemplateCli) {
@@ -30,51 +32,54 @@ fn list_handler(args: ListArgs) {
3032
fn start_handler(args: StartTemplateArgs) {
3133
let templates = parse_template_config();
3234
let template = templates
33-
.iter()
34-
.find(|&temp| temp.name == args.name)
35+
.into_iter()
36+
.find(|temp| temp.name == args.template_name)
3537
.exit(1, "No template found");
3638

39+
let detached = args.detached;
3740
let name = &template.name;
41+
3842
if session_exists(name).unwrap_or(false) {
39-
Tmux::new()
40-
.add_command(attach(name))
43+
apply_if!(!detached, Tmux::new(), add_command, attach(name))
4144
.output()
4245
.exit(1, "Could not attach to the Tmux-session");
4346
return;
4447
}
4548

46-
let initial_tmux = Tmux::new()
47-
.add_command(NewSession::new().detached().session_name(name))
48-
.add_command(attach(name));
49-
let tmux = apply_template(initial_tmux, template);
49+
let resolved_path = args.directory.and_then(|p| absolute_path(&p).ok());
50+
let (new_session_cmd, name) = resolve_cmd_name(&resolved_path, args.name, name.to_string());
5051

51-
tmux.output().exit(1, "Could not start Tmux-session");
52-
}
52+
let initial_tmux = Tmux::new().add_command(new_session_cmd);
53+
let initial_tmux = apply_if!(!detached, initial_tmux, add_command, attach(&name));
5354

54-
fn apply_template<'a>(tmux: Tmux<'a>, template: &'a Template) -> Tmux<'a> {
55-
let enumerated = template.windows.iter().enumerate();
56-
enumerated.fold(tmux, |tmux, (window_idx, window)| {
57-
let cmd = match (window_idx, &window.name) {
58-
(0, Some(name)) => TmuxCommand::rename_window().new_name(name).into(),
59-
(0, None) => TmuxCommand::new(),
60-
(_, Some(name)) => TmuxCommand::new_window().window_name(name).into(),
61-
(_, None) => TmuxCommand::new_window().into(),
62-
};
55+
let tmux = apply_template(initial_tmux, &template, &resolved_path);
6356

64-
let tmux = tmux.add_command(cmd);
65-
add_panes_to_tmux(tmux, &window.panes)
66-
})
57+
tmux.output().exit(1, "Could not start Tmux-session");
6758
}
6859

69-
fn add_panes_to_tmux<'a>(tmux: Tmux<'a>, panes: &[String]) -> Tmux<'a> {
70-
let enumerated = panes.iter().enumerate();
60+
fn resolve_cmd_name(
61+
path: &Option<PathBuf>,
62+
name: Option<String>,
63+
template_name: String,
64+
) -> (TmuxCommand<'static>, String) {
65+
if let Some(p) = path {
66+
let session_name = name.unwrap_or_else(|| dir_name(p));
67+
return (
68+
NewSession::new()
69+
.detached()
70+
.session_name(session_name.clone())
71+
.start_directory(p.to_string_lossy().into_owned())
72+
.into(),
73+
session_name,
74+
);
75+
}
7176

72-
enumerated.fold(tmux, |tmux, (pane_idx, command)| {
73-
let tmux = if pane_idx > 0 {
74-
tmux.add_command(TmuxCommand::split_window())
75-
} else {
76-
tmux
77-
};
78-
tmux.add_command(TmuxCommand::send_keys().key(format!("{}\r", command)))
79-
})
77+
let session_name = name.unwrap_or(template_name);
78+
(
79+
NewSession::new()
80+
.detached()
81+
.session_name(session_name.clone())
82+
.into(),
83+
session_name,
84+
)
8085
}

src/helpers.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ pub fn runs_in_tmux() -> bool {
5757
env::var("TMUX").is_ok()
5858
}
5959

60+
pub fn dir_name(path: &Path) -> String {
61+
path.file_name()
62+
.and_then(|os_string| os_string.to_str())
63+
.map(|str| str.to_string())
64+
.unwrap_or("".to_string())
65+
}
66+
6067
#[cfg(test)]
6168
mod tests {
6269
use super::*;

src/macros.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,14 @@ macro_rules! exit {
55
std::process::exit($code);
66
}};
77
}
8+
9+
#[macro_export]
10+
macro_rules! apply_if {
11+
($condition:expr, $obj:expr, $method:ident $(, $args:expr)*) => {
12+
if $condition {
13+
$obj.$method($($args),*)
14+
} else {
15+
$obj
16+
}
17+
};
18+
}

src/templates.rs

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use crate::{
33
widgets::table::Table,
44
};
55
use serde::Deserialize;
6-
use std::fs;
6+
use std::{fs, path::PathBuf};
7+
use tmux_interface::{Tmux, TmuxCommand};
78

89
#[derive(Deserialize, Debug)]
910
pub struct Template {
@@ -14,6 +15,7 @@ pub struct Template {
1415
#[derive(Deserialize, Debug)]
1516
pub struct Window {
1617
pub name: Option<String>,
18+
pub layout: Option<String>,
1719
pub panes: Vec<String>,
1820
}
1921

@@ -40,3 +42,61 @@ pub fn parse_template_config() -> Vec<Template> {
4042
.filter_map(|x| serde_yaml::from_str::<Template>(x).ok())
4143
.collect()
4244
}
45+
46+
pub fn apply_template<'a>(
47+
tmux: Tmux<'a>,
48+
template: &'a Template,
49+
dir: &'a Option<PathBuf>,
50+
) -> Tmux<'a> {
51+
let enumerated = template.windows.iter().enumerate();
52+
enumerated.fold(tmux, |tmux, (window_idx, window)| {
53+
let cmd = build_tmux_command(window_idx, window, dir);
54+
55+
let layout_cmd: TmuxCommand = match &window.layout {
56+
Some(layout) => TmuxCommand::select_layout().layout_name(layout).into(),
57+
None => TmuxCommand::select_layout().into(),
58+
};
59+
60+
let tmux = tmux.add_command(cmd);
61+
add_panes_to_tmux(tmux, &window.panes, dir).add_command(layout_cmd)
62+
})
63+
}
64+
65+
fn add_panes_to_tmux<'a>(tmux: Tmux<'a>, panes: &[String], dir: &'a Option<PathBuf>) -> Tmux<'a> {
66+
let enumerated = panes.iter().enumerate();
67+
68+
enumerated.fold(tmux, |tmux, (pane_idx, command)| {
69+
let cmd: TmuxCommand = match (pane_idx, dir) {
70+
(0, _) => TmuxCommand::new(),
71+
(_, Some(d)) => TmuxCommand::split_window()
72+
.start_directory(d.to_string_lossy())
73+
.into(),
74+
(_, None) => TmuxCommand::split_window().into(),
75+
};
76+
77+
tmux.add_command(cmd)
78+
.add_command(TmuxCommand::send_keys().key(format!("{}\r", command)))
79+
})
80+
}
81+
82+
fn build_tmux_command<'a>(
83+
window_idx: usize,
84+
window: &'a Window,
85+
dir: &'a Option<PathBuf>,
86+
) -> TmuxCommand<'a> {
87+
if window_idx == 0 {
88+
match &window.name {
89+
Some(name) => TmuxCommand::rename_window().new_name(name).into(),
90+
None => TmuxCommand::new(),
91+
}
92+
} else {
93+
let new_win = match &window.name {
94+
Some(name) => TmuxCommand::new_window().window_name(name),
95+
None => TmuxCommand::new_window(),
96+
};
97+
match dir {
98+
Some(d) => new_win.start_directory(d.to_string_lossy()).into(),
99+
None => new_win.into(),
100+
}
101+
}
102+
}

0 commit comments

Comments
 (0)