Compare commits
22 commits
0fc6cd66c6
...
2ccf9fa987
Author | SHA1 | Date | |
---|---|---|---|
2ccf9fa987 | |||
80a5c0a968 | |||
18dd30f101 | |||
4f372ead42 | |||
f16554cd59 | |||
f8c3ac20ac | |||
5d60b35d07 | |||
b380d1863d | |||
b7f5e8d045 | |||
58ff8fde2a | |||
087a2fe66e | |||
478638d249 | |||
50f129c793 | |||
fd076f7fee | |||
fa2fdeb5bb | |||
54f3fbb331 | |||
0a17e66b4b | |||
f7cbf8dbe5 | |||
802daeba37 | |||
708c2334e2 | |||
71b33ca274 | |||
aa4c26ba4c |
9 changed files with 137 additions and 39 deletions
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "remux"
|
name = "remux"
|
||||||
version = "0.3.0"
|
version = "0.3.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = [ "Valerie Wolfe <sleeplessval@gmail.com>" ]
|
authors = [ "Valerie Wolfe <sleeplessval@gmail.com>" ]
|
||||||
description = "A friendly command shortener for tmux"
|
description = "A friendly command shortener for tmux"
|
||||||
|
|
19
README.md
19
README.md
|
@ -17,10 +17,10 @@ shorter than its equivalent tmux command:
|
||||||
```sh
|
```sh
|
||||||
|
|
||||||
# new session
|
# new session
|
||||||
tmux new-session -t foo
|
tmux new-s -t foo
|
||||||
remux n foo
|
remux n foo
|
||||||
|
|
||||||
# lists
|
# list sessions
|
||||||
tmux ls
|
tmux ls
|
||||||
remux l
|
remux l
|
||||||
remux
|
remux
|
||||||
|
@ -30,11 +30,11 @@ tmux a -t foo
|
||||||
remux a foo
|
remux a foo
|
||||||
|
|
||||||
# has
|
# has
|
||||||
tmux has -t foo
|
tmux h -t foo
|
||||||
remux has foo
|
remux h foo
|
||||||
|
|
||||||
# detach
|
# detach
|
||||||
tmux detach-client -t foo
|
tmux det -t foo
|
||||||
remux d foo
|
remux d foo
|
||||||
|
|
||||||
# nesting sessions with '-n' flag
|
# nesting sessions with '-n' flag
|
||||||
|
@ -43,6 +43,15 @@ remux a -n foo
|
||||||
TMUX='' tmux new-session -t foo
|
TMUX='' tmux new-session -t foo
|
||||||
remux n -n foo
|
remux n -n foo
|
||||||
|
|
||||||
|
# switch to another session
|
||||||
|
tmux swi -t foo
|
||||||
|
rmux s foo
|
||||||
|
|
||||||
|
# cd to session path
|
||||||
|
tmux run 'printf "#{session_path}" > /tmp/tmux_path'
|
||||||
|
cd `cat /tmp/tmux_path`
|
||||||
|
cd `rmux p`
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
//! commands accessible from within a session
|
//! commands accessible from within a session
|
||||||
|
use std::fs::read_to_string;
|
||||||
|
|
||||||
use pico_args::Arguments;
|
use pico_args::Arguments;
|
||||||
use tmux_interface::{
|
use tmux_interface::{
|
||||||
|
@ -8,7 +9,10 @@ use tmux_interface::{
|
||||||
|
|
||||||
use crate::{ error, flag, util };
|
use crate::{ error, flag, util };
|
||||||
|
|
||||||
|
const TMP_ROOT: &str = "/tmp/remux_path";
|
||||||
|
|
||||||
pub fn switch(pargs: &mut Arguments) {
|
pub fn switch(pargs: &mut Arguments) {
|
||||||
|
util::terminal_enforce();
|
||||||
// refuse to run outside a session
|
// refuse to run outside a session
|
||||||
util::session_enforce("switch");
|
util::session_enforce("switch");
|
||||||
|
|
||||||
|
@ -32,3 +36,17 @@ pub fn switch(pargs: &mut Arguments) {
|
||||||
.output().ok();
|
.output().ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn path() {
|
||||||
|
util::session_enforce("path");
|
||||||
|
|
||||||
|
let exec = commands::Run::new().shell_command("printf '#{session_path}' > ".to_string() + TMP_ROOT);
|
||||||
|
Tmux::new()
|
||||||
|
.add_command(exec)
|
||||||
|
.output().ok();
|
||||||
|
|
||||||
|
if let Ok(text) = read_to_string(TMP_ROOT) {
|
||||||
|
println!("{text}");
|
||||||
|
std::fs::remove_file(TMP_ROOT).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
//! globally available tmux commands.
|
//! globally available tmux commands.
|
||||||
use std::{
|
use std::{
|
||||||
env::var,
|
|
||||||
ffi::OsString,
|
ffi::OsString,
|
||||||
process::exit
|
process::exit
|
||||||
};
|
};
|
||||||
|
@ -12,15 +11,22 @@ use tmux_interface::{
|
||||||
commands
|
commands
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{ error, flag, util };
|
use crate::{
|
||||||
|
env::{ self, env_var },
|
||||||
|
error,
|
||||||
|
flag,
|
||||||
|
util
|
||||||
|
};
|
||||||
|
|
||||||
pub fn attach(pargs: &mut Arguments) {
|
pub fn attach(pargs: &mut Arguments) {
|
||||||
|
// must be run from terminal
|
||||||
|
util::terminal_enforce();
|
||||||
// don't allow unflagged nests
|
// don't allow unflagged nests
|
||||||
util::prevent_nest();
|
util::prevent_nest();
|
||||||
|
|
||||||
// consume optional flags
|
// consume optional flags
|
||||||
let read_only = pargs.contains(flag::READ_ONLY);
|
let read_only = pargs.contains(flag::READ_ONLY);
|
||||||
let detach_other = pargs.contains(flag::DETACHED);
|
let detach_other = pargs.contains(flag::DETACH);
|
||||||
|
|
||||||
let args = pargs.clone().finish();
|
let args = pargs.clone().finish();
|
||||||
let target: String;
|
let target: String;
|
||||||
|
@ -59,6 +65,7 @@ pub fn attach(pargs: &mut Arguments) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn detach(pargs: &mut Arguments) {
|
pub fn detach(pargs: &mut Arguments) {
|
||||||
|
util::terminal_enforce();
|
||||||
// get target or fallback
|
// get target or fallback
|
||||||
let args = pargs.clone().finish();
|
let args = pargs.clone().finish();
|
||||||
let target: String;
|
let target: String;
|
||||||
|
@ -119,7 +126,7 @@ pub fn list() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// get attached session symbol
|
// get attached session symbol
|
||||||
let attach_symbol = var("REMUX_ATTACH_SYMBOL").unwrap_or("*".to_string());
|
let attach_symbol = env_var(env::ATTACH_SYMBOL);
|
||||||
|
|
||||||
// pretty print session list
|
// pretty print session list
|
||||||
println!("sessions:");
|
println!("sessions:");
|
||||||
|
@ -142,12 +149,17 @@ pub fn list() {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(pargs: &mut Arguments) {
|
pub fn new(pargs: &mut Arguments) {
|
||||||
|
util::terminal_enforce();
|
||||||
// don't allow unflagged nesting
|
// don't allow unflagged nesting
|
||||||
util::prevent_nest();
|
util::prevent_nest();
|
||||||
|
|
||||||
// get optional flag
|
// get optional flags
|
||||||
|
let detached = pargs.contains(flag::DETACH);
|
||||||
let target_dir: Result<String, Error> = pargs.value_from_str(flag::TARGET);
|
let target_dir: Result<String, Error> = pargs.value_from_str(flag::TARGET);
|
||||||
|
|
||||||
|
// get environment variables
|
||||||
|
let window_name = env_var(env::NEW_WINDOW_NAME);
|
||||||
|
|
||||||
// get target or fallback
|
// get target or fallback
|
||||||
let args = pargs.clone().finish();
|
let args = pargs.clone().finish();
|
||||||
let title: String;
|
let title: String;
|
||||||
|
@ -164,10 +176,18 @@ pub fn new(pargs: &mut Arguments) {
|
||||||
let mut new = commands::NewSession::new();
|
let mut new = commands::NewSession::new();
|
||||||
new = new.group_name(title);
|
new = new.group_name(title);
|
||||||
if let Some(command) = command { new.shell_command = Some(command.to_string_lossy()); }
|
if let Some(command) = command { new.shell_command = Some(command.to_string_lossy()); }
|
||||||
|
if detached { new.detached = true; }
|
||||||
if let Ok(target_dir) = target_dir { new = new.start_directory(target_dir); }
|
if let Ok(target_dir) = target_dir { new = new.start_directory(target_dir); }
|
||||||
|
|
||||||
Tmux::new()
|
let mut tmux = Tmux::new().add_command(new);
|
||||||
.add_command(new)
|
|
||||||
.output().ok();
|
// rename window if var not empty
|
||||||
|
if !window_name.is_empty() {
|
||||||
|
let auto_name = commands::RenameWindow::new()
|
||||||
|
.new_name(window_name);
|
||||||
|
tmux = tmux.add_command(auto_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
tmux.output().ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
13
src/env.rs
Normal file
13
src/env.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
use std::env::var;
|
||||||
|
|
||||||
|
pub type EnvVar = (&'static str, &'static str);
|
||||||
|
|
||||||
|
pub static ATTACH_SYMBOL: EnvVar = ("REMUX_ATTACH_SYMBOL", "*");
|
||||||
|
pub static NEW_WINDOW_NAME: EnvVar = ("REMUX_NEW_WINDOW", "");
|
||||||
|
|
||||||
|
pub fn env_var(envvar: EnvVar) -> String {
|
||||||
|
var(envvar.0).unwrap_or(envvar.1.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tmux() -> bool { !var("TMUX").unwrap_or("".to_string()).is_empty() }
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
|
||||||
type Flag = [&'static str;2];
|
type Flag = [&'static str;2];
|
||||||
|
|
||||||
pub static DETACHED: Flag = ["-d", "--detached"];
|
pub static DETACH: Flag = ["-d", "--detach"];
|
||||||
pub static HELP: Flag = ["-h", "--help"];
|
pub static HELP: Flag = ["-h", "--help"];
|
||||||
pub static NEST: Flag = ["-n", "--nest"];
|
pub static NEST: Flag = ["-n", "--nest"];
|
||||||
pub static QUIET: Flag = ["-q", "--quiet"];
|
pub static QUIET: Flag = ["-q", "--quiet"];
|
||||||
|
|
41
src/help.rs
41
src/help.rs
|
@ -16,14 +16,23 @@ A command wrapper for tmux written in Rust.
|
||||||
usage: remux <command> [<args>]
|
usage: remux <command> [<args>]
|
||||||
|
|
||||||
commands:
|
commands:
|
||||||
help Show help text for remux or a specific command
|
help Show help text for remux, a command, or a help topic.
|
||||||
attach Attach to an existing tmux session
|
attach Attach to an existing tmux session
|
||||||
detach Detach clients from a tmux session
|
detach Detach clients from a tmux session
|
||||||
has Check if a tmux session exists
|
has Check if a tmux session exists
|
||||||
list Pretty-print all tmux sessions
|
list Pretty-print all tmux sessions
|
||||||
new Create a new tmux session
|
new Create a new tmux session
|
||||||
|
|
||||||
Use 'remux help <command>' to see detailed help text for each command."),
|
path print session path (session)
|
||||||
|
switch switch to another session (session)
|
||||||
|
|
||||||
|
Use 'remux help <command>' to see detailed help text for each command.
|
||||||
|
|
||||||
|
help topics:
|
||||||
|
env Environment variables"),
|
||||||
|
|
||||||
|
|
||||||
|
// COMMAND HELP
|
||||||
|
|
||||||
Some("a" | "attach")
|
Some("a" | "attach")
|
||||||
=>
|
=>
|
||||||
|
@ -53,12 +62,13 @@ usage: remux detach <session>
|
||||||
args:
|
args:
|
||||||
<session> The session name to detach clients from"),
|
<session> The session name to detach clients from"),
|
||||||
|
|
||||||
Some("has")
|
Some("h" | "has")
|
||||||
=>
|
=>
|
||||||
println!("remux has
|
println!("remux has
|
||||||
Check if the target session exists.
|
Check if the target session exists.
|
||||||
|
|
||||||
usage: remux has [flags] <session>
|
usage: remux has [flags] <session>
|
||||||
|
rmux h [flags] session
|
||||||
|
|
||||||
args:
|
args:
|
||||||
<session> The session to check for
|
<session> The session to check for
|
||||||
|
@ -91,6 +101,15 @@ flags:
|
||||||
-n, --nest Create the session inside another session.
|
-n, --nest Create the session inside another session.
|
||||||
-t, --target <dir> Sets the target directory for the new session."),
|
-t, --target <dir> Sets the target directory for the new session."),
|
||||||
|
|
||||||
|
Some("root")
|
||||||
|
=>
|
||||||
|
println!("remux path
|
||||||
|
Print the session path (#{{session_path}}) to standard output.
|
||||||
|
Must be run from inside a session.
|
||||||
|
|
||||||
|
usage: remux path
|
||||||
|
remux p"),
|
||||||
|
|
||||||
Some("s" | "switch")
|
Some("s" | "switch")
|
||||||
=>
|
=>
|
||||||
println!("remux switch
|
println!("remux switch
|
||||||
|
@ -106,6 +125,22 @@ args:
|
||||||
flags:
|
flags:
|
||||||
-r, --read-only Attach the target session as read-only."),
|
-r, --read-only Attach the target session as read-only."),
|
||||||
|
|
||||||
|
// TOPIC HELP
|
||||||
|
|
||||||
|
Some("env" | "vars")
|
||||||
|
=>
|
||||||
|
println!("remux environment variables
|
||||||
|
|
||||||
|
REMUX_ATTACH_SYMBOL
|
||||||
|
Changes the symbol displayed for attached sessions displayed
|
||||||
|
by the 'list' command.
|
||||||
|
Default: '*'
|
||||||
|
|
||||||
|
REMUX_NEW_WINDOW
|
||||||
|
Provides a default window name when creating a session with
|
||||||
|
the 'new' command, if not empty.
|
||||||
|
Default: ''"),
|
||||||
|
|
||||||
// not found
|
// not found
|
||||||
_ => error::no_help(topic.unwrap())
|
_ => error::no_help(topic.unwrap())
|
||||||
}
|
}
|
||||||
|
|
15
src/main.rs
15
src/main.rs
|
@ -1,11 +1,9 @@
|
||||||
use std::{
|
use std::env::{ set_var, var };
|
||||||
env::{ set_var, var },
|
|
||||||
io::{ stdout, IsTerminal }
|
|
||||||
};
|
|
||||||
|
|
||||||
use pico_args::Arguments;
|
use pico_args::Arguments;
|
||||||
|
|
||||||
mod command;
|
mod command;
|
||||||
|
mod env;
|
||||||
mod error;
|
mod error;
|
||||||
mod flag;
|
mod flag;
|
||||||
mod help;
|
mod help;
|
||||||
|
@ -39,13 +37,11 @@ fn main() {
|
||||||
set_var("TMUX", "");
|
set_var("TMUX", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
if !stdout().is_terminal() { error::not_terminal(); }
|
|
||||||
|
|
||||||
let subcommand = args.subcommand().unwrap();
|
let subcommand = args.subcommand().unwrap();
|
||||||
|
|
||||||
// invoke subcommand function
|
// invoke subcommand function
|
||||||
match subcommand.as_deref() {
|
match subcommand.as_deref() {
|
||||||
Some("h" | "help")
|
Some("help")
|
||||||
=> help(&mut args),
|
=> help(&mut args),
|
||||||
|
|
||||||
Some("a" | "attach")
|
Some("a" | "attach")
|
||||||
|
@ -54,7 +50,7 @@ fn main() {
|
||||||
Some("d" | "detach")
|
Some("d" | "detach")
|
||||||
=> command::share::detach(&mut args),
|
=> command::share::detach(&mut args),
|
||||||
|
|
||||||
Some("has")
|
Some("h" | "has")
|
||||||
=> command::share::has(&mut args),
|
=> command::share::has(&mut args),
|
||||||
|
|
||||||
None |
|
None |
|
||||||
|
@ -64,6 +60,9 @@ fn main() {
|
||||||
Some("n" | "new")
|
Some("n" | "new")
|
||||||
=> command::share::new(&mut args),
|
=> command::share::new(&mut args),
|
||||||
|
|
||||||
|
Some("p" | "path")
|
||||||
|
=> command::session::path(),
|
||||||
|
|
||||||
Some("s" | "switch")
|
Some("s" | "switch")
|
||||||
=> command::session::switch(&mut args),
|
=> command::session::switch(&mut args),
|
||||||
|
|
||||||
|
|
26
src/util.rs
26
src/util.rs
|
@ -1,5 +1,6 @@
|
||||||
use std::{
|
use std::{
|
||||||
env::{ current_dir, var },
|
env::current_dir,
|
||||||
|
io::{ stdout, IsTerminal },
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
process::exit
|
process::exit
|
||||||
};
|
};
|
||||||
|
@ -11,7 +12,10 @@ use tmux_interface::{
|
||||||
variables::session::SessionsCtl
|
variables::session::SessionsCtl
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::error;
|
use crate::{
|
||||||
|
env,
|
||||||
|
error
|
||||||
|
};
|
||||||
|
|
||||||
/// return a Vec of all sessions or None
|
/// return a Vec of all sessions or None
|
||||||
pub fn get_sessions() -> Option<Vec<Session>> {
|
pub fn get_sessions() -> Option<Vec<Session>> {
|
||||||
|
@ -23,19 +27,15 @@ pub fn get_sessions() -> Option<Vec<Session>> {
|
||||||
|
|
||||||
/// show the tmux nest text if env var is not unset
|
/// show the tmux nest text if env var is not unset
|
||||||
pub fn prevent_nest() {
|
pub fn prevent_nest() {
|
||||||
let tmux = var("TMUX").ok();
|
if env::tmux() {
|
||||||
if tmux.is_some() && tmux.unwrap() != "" {
|
println!("To nest sessions, use the -n flag.");
|
||||||
println!("Sessions should be nested with care; unset TMUX or use the '-n' flag to allow.");
|
exit(6);
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// enforce a command is being used in-session
|
/// enforce a command is being used in-session
|
||||||
pub fn session_enforce(cmd: &'static str) {
|
pub fn session_enforce(cmd: &'static str) {
|
||||||
let tmux = var("TMUX").unwrap_or("".to_string());
|
if !env::tmux() { error::not_in_session(cmd); }
|
||||||
if tmux.is_empty() {
|
|
||||||
error::not_in_session(cmd);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// check whether a target session exists
|
/// check whether a target session exists
|
||||||
|
@ -43,12 +43,16 @@ pub fn session_exists<S: Into<String>>(target: S) -> bool {
|
||||||
let has_session = commands::HasSession::new()
|
let has_session = commands::HasSession::new()
|
||||||
.target_session(target.into());
|
.target_session(target.into());
|
||||||
Tmux::new().add_command(has_session)
|
Tmux::new().add_command(has_session)
|
||||||
.disable_echo()
|
|
||||||
.status()
|
.status()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.success()
|
.success()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// enforce a command is being run in a terminal
|
||||||
|
pub fn terminal_enforce() {
|
||||||
|
if !stdout().is_terminal() { error::not_terminal(); }
|
||||||
|
}
|
||||||
|
|
||||||
/// attempt to return the repo name or exit
|
/// attempt to return the repo name or exit
|
||||||
pub fn repo_fallback() -> String {
|
pub fn repo_fallback() -> String {
|
||||||
let repo = repo_root(current_dir().unwrap());
|
let repo = repo_root(current_dir().unwrap());
|
||||||
|
|
Loading…
Reference in a new issue