mirror of
https://github.com/rust-lang/rustlings.git
synced 2025-04-18 13:38:36 +01:00
init
This commit is contained in:
parent
fbfd4f25e7
commit
d55d2adef7
5 changed files with 47 additions and 4 deletions
|
@ -4,6 +4,7 @@ use crossterm::{
|
|||
QueueableCommand,
|
||||
};
|
||||
use std::io::{self, StdoutLock, Write};
|
||||
use std::process::Command;
|
||||
|
||||
use crate::{
|
||||
cmd::CmdRunner,
|
||||
|
@ -79,6 +80,14 @@ impl Exercise {
|
|||
|
||||
writer.write_str(self.path)
|
||||
}
|
||||
|
||||
/// Open the exercise file in the specified editor
|
||||
pub fn open_in_editor(&self, editor_cmd: &str) -> io::Result<bool> {
|
||||
dbg!(editor_cmd);
|
||||
dbg!(self.path);
|
||||
let status = Command::new(editor_cmd).arg(self.path).status()?;
|
||||
Ok(status.success())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RunnableExercise {
|
||||
|
|
|
@ -35,6 +35,9 @@ struct Args {
|
|||
/// Only use this if Rustlings fails to detect exercise file changes.
|
||||
#[arg(long)]
|
||||
manual_run: bool,
|
||||
/// Command to open exercise files in an editor (e.g. "code" for VS Code)
|
||||
#[arg(long)]
|
||||
edit_cmd: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
|
@ -135,7 +138,11 @@ fn main() -> Result<ExitCode> {
|
|||
)
|
||||
};
|
||||
|
||||
watch::watch(&mut app_state, notify_exercise_names)?;
|
||||
watch::watch(
|
||||
&mut app_state,
|
||||
notify_exercise_names,
|
||||
args.edit_cmd.as_deref(),
|
||||
)?;
|
||||
}
|
||||
Some(Subcommands::Run { name }) => {
|
||||
if let Some(name) = name {
|
||||
|
|
12
src/watch.rs
12
src/watch.rs
|
@ -62,6 +62,7 @@ enum WatchExit {
|
|||
fn run_watch(
|
||||
app_state: &mut AppState,
|
||||
notify_exercise_names: Option<&'static [&'static [u8]]>,
|
||||
edit_cmd: Option<&str>,
|
||||
) -> Result<WatchExit> {
|
||||
let (watch_event_sender, watch_event_receiver) = channel();
|
||||
|
||||
|
@ -113,6 +114,9 @@ fn run_watch(
|
|||
ExercisesProgress::CurrentPending => watch_state.render(&mut stdout)?,
|
||||
},
|
||||
WatchEvent::Input(InputEvent::Reset) => watch_state.reset_exercise(&mut stdout)?,
|
||||
WatchEvent::Input(InputEvent::Edit) => {
|
||||
watch_state.edit_exercise(&mut stdout, edit_cmd)?
|
||||
}
|
||||
WatchEvent::Input(InputEvent::Quit) => {
|
||||
stdout.write_all(QUIT_MSG)?;
|
||||
break;
|
||||
|
@ -136,9 +140,10 @@ fn run_watch(
|
|||
fn watch_list_loop(
|
||||
app_state: &mut AppState,
|
||||
notify_exercise_names: Option<&'static [&'static [u8]]>,
|
||||
edit_cmd: Option<&str>,
|
||||
) -> Result<()> {
|
||||
loop {
|
||||
match run_watch(app_state, notify_exercise_names)? {
|
||||
match run_watch(app_state, notify_exercise_names, edit_cmd)? {
|
||||
WatchExit::Shutdown => break Ok(()),
|
||||
// It is much easier to exit the watch mode, launch the list mode and then restart
|
||||
// the watch mode instead of trying to pause the watch threads and correct the
|
||||
|
@ -152,6 +157,7 @@ fn watch_list_loop(
|
|||
pub fn watch(
|
||||
app_state: &mut AppState,
|
||||
notify_exercise_names: Option<&'static [&'static [u8]]>,
|
||||
edit_cmd: Option<&str>,
|
||||
) -> Result<()> {
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
|
@ -163,7 +169,7 @@ pub fn watch(
|
|||
rustix::termios::LocalModes::ICANON | rustix::termios::LocalModes::ECHO;
|
||||
rustix::termios::tcsetattr(stdin_fd, rustix::termios::OptionalActions::Now, &termios)?;
|
||||
|
||||
let res = watch_list_loop(app_state, notify_exercise_names);
|
||||
let res = watch_list_loop(app_state, notify_exercise_names, edit_cmd);
|
||||
|
||||
termios.local_modes = original_local_modes;
|
||||
rustix::termios::tcsetattr(stdin_fd, rustix::termios::OptionalActions::Now, &termios)?;
|
||||
|
@ -172,7 +178,7 @@ pub fn watch(
|
|||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
watch_list_loop(app_state, notify_exercise_names)
|
||||
watch_list_loop(app_state, notify_exercise_names, edit_cmd)
|
||||
}
|
||||
|
||||
const QUIT_MSG: &[u8] = b"
|
||||
|
|
|
@ -199,6 +199,7 @@ impl<'a> WatchState<'a> {
|
|||
show_key(b'l', b":list / ")?;
|
||||
show_key(b'c', b":check all / ")?;
|
||||
show_key(b'x', b":reset / ")?;
|
||||
show_key(b'e', b":edit / ")?;
|
||||
show_key(b'q', b":quit ? ")?;
|
||||
|
||||
stdout.flush()
|
||||
|
@ -268,6 +269,24 @@ impl<'a> WatchState<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn edit_exercise(
|
||||
&mut self,
|
||||
stdout: &mut StdoutLock,
|
||||
editor_cmd: Option<&str>,
|
||||
) -> io::Result<()> {
|
||||
if let Some(editor_cmd) = editor_cmd {
|
||||
if let Err(e) = self.app_state.current_exercise().open_in_editor(editor_cmd) {
|
||||
writeln!(stdout, "Failed to open editor: {}", e)?;
|
||||
}
|
||||
} else {
|
||||
writeln!(
|
||||
stdout,
|
||||
"No editor command specified. Use --edit-cmd to specify an editor."
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn check_all_exercises(&mut self, stdout: &mut StdoutLock) -> Result<ExercisesProgress> {
|
||||
// Ignore any input until checking all exercises is done.
|
||||
let _input_pause_guard = InputPauseGuard::scoped_pause();
|
||||
|
|
|
@ -14,6 +14,7 @@ pub enum InputEvent {
|
|||
CheckAll,
|
||||
Reset,
|
||||
Quit,
|
||||
Edit,
|
||||
}
|
||||
|
||||
pub fn terminal_event_handler(
|
||||
|
@ -51,6 +52,7 @@ pub fn terminal_event_handler(
|
|||
|
||||
continue;
|
||||
}
|
||||
KeyCode::Char('e') => InputEvent::Edit,
|
||||
KeyCode::Char('q') => break WatchEvent::Input(InputEvent::Quit),
|
||||
_ => continue,
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue