Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 62 additions & 20 deletions src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,11 +272,53 @@ fn platform_fallback_editor() -> &'static str {
}
}

fn is_wsl() -> bool {
static CACHED: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
*CACHED.get_or_init(|| {
if std::env::var("WSL_DISTRO_NAME")
.map(|s| !s.is_empty())
.unwrap_or(false)
{
return true;
}
std::fs::read_to_string("/proc/sys/kernel/osrelease")
.map(|s| s.to_lowercase().contains("microsoft"))
.unwrap_or(false)
})
}

pub(crate) enum LaunchStrategy {
SpawnAndAssume(Command),
RunAndCheck(Command),
}

fn silence(cmd: &mut Command) {
cmd.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null());
}

impl LaunchStrategy {
fn run(self) -> bool {
match self {
LaunchStrategy::SpawnAndAssume(mut cmd) => {
silence(&mut cmd);
cmd.spawn().is_ok()
}
LaunchStrategy::RunAndCheck(mut cmd) => {
silence(&mut cmd);
cmd.status().map(|s| s.success()).unwrap_or(false)
}
}
}
}

pub(crate) fn try_new_tab_command(
editor: &str,
file: &Path,
emulator: &TerminalEmulator,
) -> Option<Command> {
wsl: bool,
) -> Option<LaunchStrategy> {
let (bin, args) = split_editor_cmd(editor);
let file_str = file.display().to_string();

Expand All @@ -292,7 +334,7 @@ pub(crate) fn try_new_tab_command(
cmd.arg(a);
}
cmd.arg(&file_str);
Some(cmd)
Some(LaunchStrategy::RunAndCheck(cmd))
}
TerminalEmulator::GnomeTerminal => {
let mut cmd = Command::new("gnome-terminal");
Expand All @@ -304,7 +346,7 @@ pub(crate) fn try_new_tab_command(
cmd.arg(a);
}
cmd.arg(&file_str);
Some(cmd)
Some(LaunchStrategy::SpawnAndAssume(cmd))
}
TerminalEmulator::MacTerminal(ref tp) => {
let app_name = if tp == "Apple_Terminal" {
Expand All @@ -327,9 +369,15 @@ pub(crate) fn try_new_tab_command(
};
let mut cmd = Command::new("osascript");
cmd.arg("-e").arg(script);
Some(cmd)
Some(LaunchStrategy::SpawnAndAssume(cmd))
}
TerminalEmulator::WindowsTerminal => {
let is_windows_bin = Path::new(bin)
.extension()
.is_some_and(|e| e.eq_ignore_ascii_case("exe"));
if wsl && !is_windows_bin {
return None;
}
let mut cmd = Command::new("wt");
cmd.arg("new-tab")
.arg("--title")
Expand All @@ -339,7 +387,7 @@ pub(crate) fn try_new_tab_command(
cmd.arg(a);
}
cmd.arg(&file_str);
Some(cmd)
Some(LaunchStrategy::SpawnAndAssume(cmd))
}
TerminalEmulator::Termux | TerminalEmulator::Unknown => None,
}
Expand All @@ -361,24 +409,18 @@ pub(crate) fn open_in_editor(
match kind {
EditorKind::Gui => {
let exec = which(bin).unwrap_or_else(|| PathBuf::from(bin));
Command::new(&exec)
.args(&args)
.arg(file)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
.map_err(|e| format!("{bin}: {e}"))?;
let mut cmd = Command::new(&exec);
cmd.args(&args).arg(file);
silence(&mut cmd);
cmd.spawn().map_err(|e| format!("{bin}: {e}"))?;
Ok(EditorResult::Opened)
}
EditorKind::Terminal => {
if let Some(mut cmd) = try_new_tab_command(editor, file, emulator) {
cmd.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null());
if cmd.spawn().is_ok() {
return Ok(EditorResult::Opened);
}
if try_new_tab_command(editor, file, emulator, is_wsl())
.map(LaunchStrategy::run)
.unwrap_or(false)
{
return Ok(EditorResult::Opened);
}
Ok(EditorResult::NeedsSameTerminal)
}
Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub(crate) use config::{config_path, LeafConfig};
#[cfg(test)]
pub(crate) use editor::{
binary_name, classify, expand_editor_placeholders, resolve_editor, selection_modifier_label,
split_editor_cmd, try_new_tab_command, EditorKind, TerminalEmulator,
split_editor_cmd, try_new_tab_command, EditorKind, LaunchStrategy, TerminalEmulator,
};
#[cfg(test)]
pub(crate) use markdown::toc::{normalize_toc, toc_levels, TocEntry};
Expand Down
58 changes: 57 additions & 1 deletion src/tests/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,67 @@ fn classify_windows_path_with_spaces() {

fn mac_tab_script(editor: &str, file: &str, term_program: &str) -> String {
let emulator = TerminalEmulator::MacTerminal(term_program.to_string());
let cmd = try_new_tab_command(editor, Path::new(file), &emulator).unwrap();
let strategy = try_new_tab_command(editor, Path::new(file), &emulator, false).unwrap();
let cmd = match strategy {
LaunchStrategy::SpawnAndAssume(cmd) => cmd,
LaunchStrategy::RunAndCheck(_) => panic!("expected SpawnAndAssume for MacTerminal"),
};
let args: Vec<_> = cmd.get_args().collect();
args[1].to_str().unwrap().to_string()
}

#[test]
fn try_new_tab_command_windows_terminal_wsl_linux_editor_returns_none() {
let emulator = TerminalEmulator::WindowsTerminal;
let strategy = try_new_tab_command("nano", Path::new("/tmp/test.md"), &emulator, true);
assert!(strategy.is_none());
}

#[test]
fn try_new_tab_command_windows_terminal_wsl_windows_editor_returns_spawn() {
let emulator = TerminalEmulator::WindowsTerminal;
let strategy =
try_new_tab_command("notepad.exe", Path::new("/tmp/test.md"), &emulator, true).unwrap();
assert!(matches!(strategy, LaunchStrategy::SpawnAndAssume(_)));
}

#[test]
fn try_new_tab_command_windows_terminal_non_wsl_returns_spawn() {
let emulator = TerminalEmulator::WindowsTerminal;
let strategy =
try_new_tab_command("nano", Path::new("/tmp/test.md"), &emulator, false).unwrap();
assert!(matches!(strategy, LaunchStrategy::SpawnAndAssume(_)));
}

#[test]
fn try_new_tab_command_kitty_returns_run_and_check() {
let emulator = TerminalEmulator::Kitty;
let strategy =
try_new_tab_command("nano", Path::new("/tmp/test.md"), &emulator, false).unwrap();
assert!(matches!(strategy, LaunchStrategy::RunAndCheck(_)));
}

#[test]
fn try_new_tab_command_gnome_returns_spawn() {
let emulator = TerminalEmulator::GnomeTerminal;
let strategy = try_new_tab_command("vim", Path::new("/tmp/test.md"), &emulator, false).unwrap();
assert!(matches!(strategy, LaunchStrategy::SpawnAndAssume(_)));
}

#[test]
fn try_new_tab_command_termux_returns_none() {
let emulator = TerminalEmulator::Termux;
let strategy = try_new_tab_command("nano", Path::new("/tmp/test.md"), &emulator, false);
assert!(strategy.is_none());
}

#[test]
fn try_new_tab_command_unknown_returns_none() {
let emulator = TerminalEmulator::Unknown;
let strategy = try_new_tab_command("nano", Path::new("/tmp/test.md"), &emulator, false);
assert!(strategy.is_none());
}

#[test]
fn new_tab_command_apple_terminal_has_printf() {
let script = mac_tab_script("nano", "/tmp/test.md", "Apple_Terminal");
Expand Down
Loading