Add configurable shell (default from /bin/bash) for command execution
This commit is contained in:
parent
1da2553b92
commit
d495d23afe
5 changed files with 68 additions and 33 deletions
|
|
@ -13,6 +13,7 @@ pub enum Focus {
|
||||||
pub struct App {
|
pub struct App {
|
||||||
pub data_dir: PathBuf,
|
pub data_dir: PathBuf,
|
||||||
pub terminal: Terminal,
|
pub terminal: Terminal,
|
||||||
|
pub shell: String,
|
||||||
pub groups: Vec<Group>,
|
pub groups: Vec<Group>,
|
||||||
pub group_index: usize,
|
pub group_index: usize,
|
||||||
pub file_index: usize,
|
pub file_index: usize,
|
||||||
|
|
@ -22,11 +23,12 @@ pub struct App {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn new(data_dir: PathBuf, terminal: Terminal) -> Self {
|
pub fn new(data_dir: PathBuf, terminal: Terminal, shell: String) -> Self {
|
||||||
let groups = load_groups(&data_dir);
|
let groups = load_groups(&data_dir);
|
||||||
App {
|
App {
|
||||||
data_dir,
|
data_dir,
|
||||||
terminal,
|
terminal,
|
||||||
|
shell,
|
||||||
groups,
|
groups,
|
||||||
group_index: 0,
|
group_index: 0,
|
||||||
file_index: 0,
|
file_index: 0,
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,13 @@ use base64::{engine::general_purpose::STANDARD, Engine};
|
||||||
use crate::config::Terminal;
|
use crate::config::Terminal;
|
||||||
use crate::parser::{CommandFile, Section};
|
use crate::parser::{CommandFile, Section};
|
||||||
|
|
||||||
pub fn run_section(section: &Section, file: &CommandFile, terminal: &Terminal, data_dir: &Path) {
|
pub fn run_section(
|
||||||
|
section: &Section,
|
||||||
|
file: &CommandFile,
|
||||||
|
terminal: &Terminal,
|
||||||
|
shell: &str,
|
||||||
|
data_dir: &Path,
|
||||||
|
) {
|
||||||
let commands = if section.is_run_all() {
|
let commands = if section.is_run_all() {
|
||||||
file.sections
|
file.sections
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -25,12 +31,12 @@ pub fn run_section(section: &Section, file: &CommandFile, terminal: &Terminal, d
|
||||||
}
|
}
|
||||||
|
|
||||||
match terminal {
|
match terminal {
|
||||||
Terminal::Ghostty => run_in_ghostty(&commands),
|
Terminal::Ghostty => run_in_ghostty(&commands, shell),
|
||||||
_ => run_via_script(&commands, terminal, data_dir),
|
_ => run_via_script(&commands, terminal, shell, data_dir),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_via_script(commands: &[String], terminal: &Terminal, data_dir: &Path) {
|
fn run_via_script(commands: &[String], terminal: &Terminal, shell: &str, data_dir: &Path) {
|
||||||
let script = commands.join("\n");
|
let script = commands.join("\n");
|
||||||
let run_dir = data_dir.join(".run");
|
let run_dir = data_dir.join(".run");
|
||||||
if fs::create_dir_all(&run_dir).is_err() {
|
if fs::create_dir_all(&run_dir).is_err() {
|
||||||
|
|
@ -57,35 +63,36 @@ fn run_via_script(commands: &[String], terminal: &Terminal, data_dir: &Path) {
|
||||||
let _ = fs::set_permissions(&script_path, fs::Permissions::from_mode(0o755));
|
let _ = fs::set_permissions(&script_path, fs::Permissions::from_mode(0o755));
|
||||||
|
|
||||||
match terminal {
|
match terminal {
|
||||||
Terminal::Iterm => run_in_iterm(&script_path),
|
Terminal::Iterm => run_in_iterm(&script_path, shell),
|
||||||
Terminal::Builtin => run_in_terminal_app(&script_path),
|
Terminal::Builtin => run_in_terminal_app(&script_path, shell),
|
||||||
Terminal::Ghostty => unreachable!(),
|
Terminal::Ghostty => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_in_ghostty(commands: &[String]) {
|
fn run_in_ghostty(commands: &[String], shell: &str) {
|
||||||
let script = commands.join("\n");
|
let script = commands.join("\n");
|
||||||
let encoded = STANDARD.encode(script);
|
let encoded = STANDARD.encode(script);
|
||||||
let shell_command = format!("echo {} | base64 -D | bash", encoded);
|
let shell_command = format!("echo {} | base64 -D | {}", encoded, shell);
|
||||||
|
|
||||||
let _ = Command::new("open")
|
let _ = Command::new("open")
|
||||||
.arg("-na")
|
.arg("-na")
|
||||||
.arg("Ghostty")
|
.arg("Ghostty")
|
||||||
.arg("--args")
|
.arg("--args")
|
||||||
.arg("-e")
|
.arg("-e")
|
||||||
.arg("bash")
|
.arg(shell)
|
||||||
.arg("-c")
|
.arg("-c")
|
||||||
.arg(shell_command)
|
.arg(shell_command)
|
||||||
.spawn();
|
.spawn();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_in_terminal_app(script_path: &Path) {
|
fn run_in_terminal_app(script_path: &Path, shell: &str) {
|
||||||
let path_str = script_path.to_string_lossy();
|
let path_str = script_path.to_string_lossy();
|
||||||
let applescript = format!(
|
let applescript = format!(
|
||||||
r#"tell application "Terminal"
|
r#"tell application "Terminal"
|
||||||
do script "clear; bash {}"
|
do script "clear; {} {}"
|
||||||
activate
|
activate
|
||||||
end tell"#,
|
end tell"#,
|
||||||
|
shell,
|
||||||
escape_applescript(&path_str)
|
escape_applescript(&path_str)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -95,16 +102,17 @@ end tell"#,
|
||||||
.spawn();
|
.spawn();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_in_iterm(script_path: &Path) {
|
fn run_in_iterm(script_path: &Path, shell: &str) {
|
||||||
let path_str = script_path.to_string_lossy();
|
let path_str = script_path.to_string_lossy();
|
||||||
let applescript = format!(
|
let applescript = format!(
|
||||||
r#"tell application "iTerm"
|
r#"tell application "iTerm"
|
||||||
create window with default profile
|
create window with default profile
|
||||||
tell current session of current window
|
tell current session of current window
|
||||||
write text "clear; bash {}"
|
write text "clear; {} {}"
|
||||||
end tell
|
end tell
|
||||||
activate
|
activate
|
||||||
end tell"#,
|
end tell"#,
|
||||||
|
shell,
|
||||||
escape_applescript(&path_str)
|
escape_applescript(&path_str)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -114,7 +122,7 @@ end tell"#,
|
||||||
.spawn();
|
.spawn();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn edit_file(path: &Path, terminal: &Terminal) {
|
pub fn edit_file(path: &Path, terminal: &Terminal, shell: &str) {
|
||||||
let editor = std::env::var("EDITOR").unwrap_or_else(|_| "nano".to_string());
|
let editor = std::env::var("EDITOR").unwrap_or_else(|_| "nano".to_string());
|
||||||
|
|
||||||
match terminal {
|
match terminal {
|
||||||
|
|
@ -124,13 +132,14 @@ pub fn edit_file(path: &Path, terminal: &Terminal) {
|
||||||
.arg("Ghostty")
|
.arg("Ghostty")
|
||||||
.arg("--args")
|
.arg("--args")
|
||||||
.arg("-e")
|
.arg("-e")
|
||||||
.arg("bash")
|
.arg(shell)
|
||||||
.arg("-c")
|
.arg("-c")
|
||||||
.arg(format!(
|
.arg(format!(
|
||||||
"cd {} && {} {}; exec bash",
|
"cd {} && {} {}; exec {}",
|
||||||
escape_shell(&std::env::current_dir().unwrap_or_default().to_string_lossy()),
|
escape_shell(&std::env::current_dir().unwrap_or_default().to_string_lossy()),
|
||||||
escape_shell(&editor),
|
escape_shell(&editor),
|
||||||
escape_shell(&path.to_string_lossy())
|
escape_shell(&path.to_string_lossy()),
|
||||||
|
shell
|
||||||
))
|
))
|
||||||
.spawn();
|
.spawn();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::path::{Path, PathBuf};
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub data_dir: PathBuf,
|
pub data_dir: PathBuf,
|
||||||
pub terminal: Terminal,
|
pub terminal: Terminal,
|
||||||
|
pub shell: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||||
|
|
@ -30,8 +30,12 @@ impl Terminal {
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn new(data_dir: Option<PathBuf>) -> Self {
|
pub fn new(data_dir: Option<PathBuf>) -> Self {
|
||||||
let data_dir = data_dir.unwrap_or_else(default_data_dir);
|
let data_dir = data_dir.unwrap_or_else(default_data_dir);
|
||||||
let terminal = load_terminal_preference(&data_dir);
|
let (terminal, shell) = load_preferences(&data_dir);
|
||||||
Config { data_dir, terminal }
|
Config {
|
||||||
|
data_dir,
|
||||||
|
terminal,
|
||||||
|
shell,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -41,19 +45,39 @@ fn default_data_dir() -> PathBuf {
|
||||||
.unwrap_or_else(|| PathBuf::from(".lines"))
|
.unwrap_or_else(|| PathBuf::from(".lines"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_terminal_preference(data_dir: &Path) -> Terminal {
|
fn default_shell() -> String {
|
||||||
|
std::env::var("SHELL")
|
||||||
|
.ok()
|
||||||
|
.and_then(|s| {
|
||||||
|
Path::new(&s)
|
||||||
|
.file_name()
|
||||||
|
.and_then(|n| n.to_str())
|
||||||
|
.map(String::from)
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| "zsh".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_preferences(data_dir: &Path) -> (Terminal, String) {
|
||||||
let config_path = data_dir.join("config.toml");
|
let config_path = data_dir.join("config.toml");
|
||||||
let content = match fs::read_to_string(&config_path) {
|
let content = match fs::read_to_string(&config_path) {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(_) => return Terminal::default(),
|
Err(_) => return (Terminal::default(), default_shell()),
|
||||||
};
|
};
|
||||||
|
|
||||||
match toml::from_str::<toml::Value>(&content) {
|
match toml::from_str::<toml::Value>(&content) {
|
||||||
Ok(value) => value
|
Ok(value) => {
|
||||||
|
let terminal = value
|
||||||
.get("terminal")
|
.get("terminal")
|
||||||
.and_then(|v| v.as_str())
|
.and_then(|v| v.as_str())
|
||||||
.and_then(Terminal::from_str)
|
.and_then(Terminal::from_str)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default();
|
||||||
Err(_) => Terminal::default(),
|
let shell = value
|
||||||
|
.get("shell")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.map(String::from)
|
||||||
|
.unwrap_or_else(default_shell);
|
||||||
|
(terminal, shell)
|
||||||
|
}
|
||||||
|
Err(_) => (Terminal::default(), default_shell()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,12 +38,12 @@ pub fn handle_event(app: &mut App, event: AppEvent) -> bool {
|
||||||
KeyCode::Char('r') => app.reload(),
|
KeyCode::Char('r') => app.reload(),
|
||||||
KeyCode::Char('e') => {
|
KeyCode::Char('e') => {
|
||||||
if let Some(file) = app.current_file() {
|
if let Some(file) = app.current_file() {
|
||||||
edit_file(&file.path, &app.terminal);
|
edit_file(&file.path, &app.terminal, &app.shell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyCode::Enter => {
|
KeyCode::Enter => {
|
||||||
if let (Some(section), Some(file)) = (app.current_section(), app.current_file()) {
|
if let (Some(section), Some(file)) = (app.current_section(), app.current_file()) {
|
||||||
run_section(section, file, &app.terminal, &app.data_dir);
|
run_section(section, file, &app.terminal, &app.shell, &app.data_dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ fn main() -> io::Result<()> {
|
||||||
eprintln!("Nepodařilo se vytvořit datovou složku {}: {}", config.data_dir.display(), e);
|
eprintln!("Nepodařilo se vytvořit datovou složku {}: {}", config.data_dir.display(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
let app = App::new(config.data_dir, config.terminal);
|
let app = App::new(config.data_dir, config.terminal, config.shell);
|
||||||
|
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
let mut stdout = io::stdout();
|
let mut stdout = io::stdout();
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue