Skip to content

Commit b5fbf59

Browse files
committed
Check if editor program exists before choosing it
1 parent 695f927 commit b5fbf59

4 files changed

Lines changed: 32 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
### Added
66

7+
- Automatically open the current file if Rustlings is running in a VS Code terminal
8+
- Automatically open the current file with `$EDITOR` in a new pane if Rustlings is running in [Zellij](https://zellij.dev)
9+
- New argument `--edit-cmd` to communicate with an editor running in a different process to open the current exercise
710
- Show the file link of the current exercise when running `rustlings hint` and `rustlings reset`
811

912
### Fixed

src/app_state.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ impl AppState {
6969
exercise_infos: Vec<ExerciseInfo>,
7070
final_message: &'static str,
7171
editor: Option<Editor>,
72+
vs_code_term: bool,
7273
) -> Result<(Self, StateFileStatus)> {
7374
let cmd_runner = CmdRunner::build()?;
7475
let mut state_file = OpenOptions::new()
@@ -178,7 +179,7 @@ impl AppState {
178179
official_exercises: !Path::new("info.toml").exists(),
179180
cmd_runner,
180181
// VS Code has its own file link handling
181-
emit_file_links: !matches!(editor, Some(Editor::VSCode)),
182+
emit_file_links: !vs_code_term,
182183
editor,
183184
};
184185

src/editor.rs

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::{
2+
borrow::Cow,
23
env,
34
process::{Command, Stdio},
45
thread::{self, JoinHandle},
@@ -28,16 +29,29 @@ fn run_cmd(cmd: &mut Command) -> Result<Vec<u8>> {
2829
Ok(output.stdout)
2930
}
3031

32+
fn program_exists(program: &str) -> bool {
33+
Command::new(program)
34+
.arg("--version")
35+
.stdin(Stdio::null())
36+
.stdout(Stdio::null())
37+
.stderr(Stdio::null())
38+
.status()
39+
.is_ok_and(|status| status.success())
40+
}
41+
3142
pub enum Editor {
32-
VSCode,
33-
Cmd(String, Vec<String>),
43+
Cmd(Cow<'static, str>, Vec<String>),
3444
Zellij(Option<(String, u32, usize)>),
3545
}
3646

3747
impl Editor {
38-
pub fn new(cmd: Option<String>) -> Result<Option<Self>> {
39-
if env::var_os("TERM_PROGRAM").is_some_and(|v| v == "vscode") {
40-
return Ok(Some(Self::VSCode));
48+
pub fn new(cmd: Option<String>, vs_code_term: bool) -> Result<Option<Self>> {
49+
if vs_code_term {
50+
for program in ["code", "codium"] {
51+
if program_exists(program) {
52+
return Ok(Some(Self::Cmd(Cow::Borrowed(program), Vec::new())));
53+
}
54+
}
4155
}
4256

4357
if let Some(cmd) = cmd {
@@ -47,10 +61,10 @@ impl Editor {
4761
if shlex.had_error {
4862
bail!("Failed to parse the command in `--edit-cmd`");
4963
}
50-
return Ok(Some(Self::Cmd(program, args)));
64+
return Ok(Some(Self::Cmd(Cow::Owned(program), args)));
5165
}
5266

53-
if env::var_os("ZELLIJ").is_some() {
67+
if env::var_os("ZELLIJ").is_some() && program_exists("zellij") {
5468
return Ok(Some(Self::Zellij(None)));
5569
}
5670

@@ -65,11 +79,8 @@ impl Editor {
6579
let handle = thread::Builder::new()
6680
.spawn(move || {
6781
match &mut self {
68-
Editor::VSCode => {
69-
run_cmd(Command::new("code").arg(exercise_path))?;
70-
}
7182
Editor::Cmd(program, args) => {
72-
run_cmd(Command::new(program).args(args).arg(exercise_path))?;
83+
run_cmd(Command::new(&**program).args(args).arg(exercise_path))?;
7384
}
7485
Editor::Zellij(open_pane) => {
7586
if let Some((pane_id_str, pane_id, open_exercise_ind)) = open_pane {
@@ -105,7 +116,7 @@ impl Editor {
105116

106117
pub fn close(&mut self) -> Result<()> {
107118
match self {
108-
Editor::VSCode | Editor::Cmd(_, _) => (),
119+
Editor::Cmd(_, _) => (),
109120
Editor::Zellij(open_pane) => {
110121
if let Some((pane_id_str, _, _)) = open_pane.take() {
111122
zellij::close_pane(&pane_id_str)?;

src/main.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use anyhow::{Context, Result, bail};
22
use app_state::StateFileStatus;
33
use clap::Parser;
44
use std::{
5+
env,
56
io::{self, IsTerminal, Write},
67
path::Path,
78
process::ExitCode,
@@ -60,11 +61,13 @@ fn main() -> Result<ExitCode> {
6061
bail!(FORMAT_VERSION_HIGHER_ERR);
6162
}
6263

63-
let editor = Editor::new(args.edit_cmd)?;
64+
let vs_code_term = env::var_os("TERM_PROGRAM").is_some_and(|v| v == "vscode");
65+
let editor = Editor::new(args.edit_cmd, vs_code_term)?;
6466
let (mut app_state, state_file_status) = AppState::new(
6567
info_file.exercises,
6668
info_file.final_message.unwrap_or_default(),
6769
editor,
70+
vs_code_term,
6871
)?;
6972

7073
// Show the welcome message if the state file doesn't exist yet.

0 commit comments

Comments
 (0)