mirror of
https://github.com/rust-lang/rustlings.git
synced 2025-09-16 09:26:49 +02:00
Fix file links in VS Code
This commit is contained in:
@@ -60,8 +60,7 @@ pub struct AppState {
|
|||||||
file_buf: Vec<u8>,
|
file_buf: Vec<u8>,
|
||||||
official_exercises: bool,
|
official_exercises: bool,
|
||||||
cmd_runner: CmdRunner,
|
cmd_runner: CmdRunner,
|
||||||
// Running in VS Code.
|
emit_file_links: bool,
|
||||||
vs_code: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppState {
|
impl AppState {
|
||||||
@@ -181,7 +180,8 @@ impl AppState {
|
|||||||
file_buf,
|
file_buf,
|
||||||
official_exercises: !Path::new("info.toml").exists(),
|
official_exercises: !Path::new("info.toml").exists(),
|
||||||
cmd_runner,
|
cmd_runner,
|
||||||
vs_code: env::var_os("TERM_PROGRAM").is_some_and(|v| v == "vscode"),
|
// VS Code has its own file link handling
|
||||||
|
emit_file_links: env::var_os("TERM_PROGRAM").is_none_or(|v| v != "vscode"),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((slf, state_file_status))
|
Ok((slf, state_file_status))
|
||||||
@@ -218,8 +218,8 @@ impl AppState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn vs_code(&self) -> bool {
|
pub fn emit_file_links(&self) -> bool {
|
||||||
self.vs_code
|
self.emit_file_links
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the state file.
|
// Write the state file.
|
||||||
@@ -621,7 +621,7 @@ mod tests {
|
|||||||
file_buf: Vec::new(),
|
file_buf: Vec::new(),
|
||||||
official_exercises: true,
|
official_exercises: true,
|
||||||
cmd_runner: CmdRunner::build().unwrap(),
|
cmd_runner: CmdRunner::build().unwrap(),
|
||||||
vs_code: false,
|
emit_file_links: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut assert = |done: [bool; 3], expected: [Option<usize>; 3]| {
|
let mut assert = |done: [bool; 3], expected: [Option<usize>; 3]| {
|
||||||
|
@@ -7,22 +7,28 @@ use std::io::{self, StdoutLock, Write};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
cmd::CmdRunner,
|
cmd::CmdRunner,
|
||||||
term::{self, CountedWrite, terminal_file_link, write_ansi},
|
term::{self, CountedWrite, file_path, terminal_file_link, write_ansi},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The initial capacity of the output buffer.
|
/// The initial capacity of the output buffer.
|
||||||
pub const OUTPUT_CAPACITY: usize = 1 << 14;
|
pub const OUTPUT_CAPACITY: usize = 1 << 14;
|
||||||
|
|
||||||
pub fn solution_link_line(stdout: &mut StdoutLock, solution_path: &str) -> io::Result<()> {
|
pub fn solution_link_line(
|
||||||
|
stdout: &mut StdoutLock,
|
||||||
|
solution_path: &str,
|
||||||
|
emit_file_links: bool,
|
||||||
|
) -> io::Result<()> {
|
||||||
stdout.queue(SetAttribute(Attribute::Bold))?;
|
stdout.queue(SetAttribute(Attribute::Bold))?;
|
||||||
stdout.write_all(b"Solution")?;
|
stdout.write_all(b"Solution")?;
|
||||||
stdout.queue(ResetColor)?;
|
stdout.queue(ResetColor)?;
|
||||||
stdout.write_all(b" for comparison: ")?;
|
stdout.write_all(b" for comparison: ")?;
|
||||||
if let Some(canonical_path) = term::canonicalize(solution_path) {
|
file_path(stdout, Color::Cyan, |writer| {
|
||||||
terminal_file_link(stdout, solution_path, &canonical_path, Color::Cyan)?;
|
if emit_file_links && let Some(canonical_path) = term::canonicalize(solution_path) {
|
||||||
} else {
|
terminal_file_link(writer, solution_path, &canonical_path)
|
||||||
stdout.write_all(solution_path.as_bytes())?;
|
} else {
|
||||||
}
|
writer.stdout().write_all(solution_path.as_bytes())
|
||||||
|
}
|
||||||
|
})?;
|
||||||
stdout.write_all(b"\n")
|
stdout.write_all(b"\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,12 +78,18 @@ pub struct Exercise {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Exercise {
|
impl Exercise {
|
||||||
pub fn terminal_file_link<'a>(&self, writer: &mut impl CountedWrite<'a>) -> io::Result<()> {
|
pub fn terminal_file_link<'a>(
|
||||||
if let Some(canonical_path) = self.canonical_path.as_deref() {
|
&self,
|
||||||
return terminal_file_link(writer, self.path, canonical_path, Color::Blue);
|
writer: &mut impl CountedWrite<'a>,
|
||||||
}
|
emit_file_links: bool,
|
||||||
|
) -> io::Result<()> {
|
||||||
writer.write_str(self.path)
|
file_path(writer, Color::Blue, |writer| {
|
||||||
|
if emit_file_links && let Some(canonical_path) = self.canonical_path.as_deref() {
|
||||||
|
terminal_file_link(writer, self.path, canonical_path)
|
||||||
|
} else {
|
||||||
|
writer.write_str(self.path)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -186,13 +186,7 @@ impl<'a> ListState<'a> {
|
|||||||
|
|
||||||
writer.write_ascii(&self.name_col_padding[exercise.name.len()..])?;
|
writer.write_ascii(&self.name_col_padding[exercise.name.len()..])?;
|
||||||
|
|
||||||
// The list links aren't shown correctly in VS Code on Windows.
|
exercise.terminal_file_link(&mut writer, self.app_state.emit_file_links())?;
|
||||||
// But VS Code shows its own links anyway.
|
|
||||||
if self.app_state.vs_code() {
|
|
||||||
writer.write_str(exercise.path)?;
|
|
||||||
} else {
|
|
||||||
exercise.terminal_file_link(&mut writer)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.write_ascii(&self.path_col_padding[exercise.path.len()..])?;
|
writer.write_ascii(&self.path_col_padding[exercise.path.len()..])?;
|
||||||
|
|
||||||
|
@@ -167,7 +167,7 @@ fn main() -> Result<ExitCode> {
|
|||||||
}
|
}
|
||||||
app_state
|
app_state
|
||||||
.current_exercise()
|
.current_exercise()
|
||||||
.terminal_file_link(&mut stdout)?;
|
.terminal_file_link(&mut stdout, app_state.emit_file_links())?;
|
||||||
stdout.write_all(b"\n")?;
|
stdout.write_all(b"\n")?;
|
||||||
|
|
||||||
return Ok(ExitCode::FAILURE);
|
return Ok(ExitCode::FAILURE);
|
||||||
|
@@ -27,7 +27,7 @@ pub fn run(app_state: &mut AppState) -> Result<ExitCode> {
|
|||||||
stdout.write_all(b"Ran ")?;
|
stdout.write_all(b"Ran ")?;
|
||||||
app_state
|
app_state
|
||||||
.current_exercise()
|
.current_exercise()
|
||||||
.terminal_file_link(&mut stdout)?;
|
.terminal_file_link(&mut stdout, app_state.emit_file_links())?;
|
||||||
stdout.write_all(b" with errors\n")?;
|
stdout.write_all(b" with errors\n")?;
|
||||||
|
|
||||||
return Ok(ExitCode::FAILURE);
|
return Ok(ExitCode::FAILURE);
|
||||||
@@ -41,7 +41,7 @@ pub fn run(app_state: &mut AppState) -> Result<ExitCode> {
|
|||||||
|
|
||||||
if let Some(solution_path) = app_state.current_solution_path()? {
|
if let Some(solution_path) = app_state.current_solution_path()? {
|
||||||
stdout.write_all(b"\n")?;
|
stdout.write_all(b"\n")?;
|
||||||
solution_link_line(&mut stdout, &solution_path)?;
|
solution_link_line(&mut stdout, &solution_path, app_state.emit_file_links())?;
|
||||||
stdout.write_all(b"\n")?;
|
stdout.write_all(b"\n")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ pub fn run(app_state: &mut AppState) -> Result<ExitCode> {
|
|||||||
stdout.write_all(b"Next exercise: ")?;
|
stdout.write_all(b"Next exercise: ")?;
|
||||||
app_state
|
app_state
|
||||||
.current_exercise()
|
.current_exercise()
|
||||||
.terminal_file_link(&mut stdout)?;
|
.terminal_file_link(&mut stdout, app_state.emit_file_links())?;
|
||||||
stdout.write_all(b"\n")?;
|
stdout.write_all(b"\n")?;
|
||||||
}
|
}
|
||||||
ExercisesProgress::AllDone => (),
|
ExercisesProgress::AllDone => (),
|
||||||
|
29
src/term.rs
29
src/term.rs
@@ -272,22 +272,18 @@ pub fn canonicalize(path: &str) -> Option<String> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn terminal_file_link<'a>(
|
pub fn file_path<'a, W: CountedWrite<'a>>(
|
||||||
writer: &mut impl CountedWrite<'a>,
|
writer: &mut W,
|
||||||
path: &str,
|
|
||||||
canonical_path: &str,
|
|
||||||
color: Color,
|
color: Color,
|
||||||
|
f: impl FnOnce(&mut W) -> io::Result<()>,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
writer
|
writer
|
||||||
.stdout()
|
.stdout()
|
||||||
.queue(SetForegroundColor(color))?
|
.queue(SetForegroundColor(color))?
|
||||||
.queue(SetAttribute(Attribute::Underlined))?;
|
.queue(SetAttribute(Attribute::Underlined))?;
|
||||||
writer.stdout().write_all(b"\x1b]8;;file://")?;
|
|
||||||
writer.stdout().write_all(canonical_path.as_bytes())?;
|
f(writer)?;
|
||||||
writer.stdout().write_all(b"\x1b\\")?;
|
|
||||||
// Only this part is visible.
|
|
||||||
writer.write_str(path)?;
|
|
||||||
writer.stdout().write_all(b"\x1b]8;;\x1b\\")?;
|
|
||||||
writer
|
writer
|
||||||
.stdout()
|
.stdout()
|
||||||
.queue(SetForegroundColor(Color::Reset))?
|
.queue(SetForegroundColor(Color::Reset))?
|
||||||
@@ -296,6 +292,19 @@ pub fn terminal_file_link<'a>(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn terminal_file_link<'a>(
|
||||||
|
writer: &mut impl CountedWrite<'a>,
|
||||||
|
path: &str,
|
||||||
|
canonical_path: &str,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
writer.stdout().write_all(b"\x1b]8;;file://")?;
|
||||||
|
writer.stdout().write_all(canonical_path.as_bytes())?;
|
||||||
|
writer.stdout().write_all(b"\x1b\\")?;
|
||||||
|
// Only this part is visible.
|
||||||
|
writer.write_str(path)?;
|
||||||
|
writer.stdout().write_all(b"\x1b]8;;\x1b\\")
|
||||||
|
}
|
||||||
|
|
||||||
pub fn write_ansi(output: &mut Vec<u8>, command: impl Command) {
|
pub fn write_ansi(output: &mut Vec<u8>, command: impl Command) {
|
||||||
struct FmtWriter<'a>(&'a mut Vec<u8>);
|
struct FmtWriter<'a>(&'a mut Vec<u8>);
|
||||||
|
|
||||||
|
@@ -233,7 +233,7 @@ impl<'a> WatchState<'a> {
|
|||||||
stdout.write_all(b"\n")?;
|
stdout.write_all(b"\n")?;
|
||||||
|
|
||||||
if let DoneStatus::DoneWithSolution(solution_path) = &self.done_status {
|
if let DoneStatus::DoneWithSolution(solution_path) = &self.done_status {
|
||||||
solution_link_line(stdout, solution_path)?;
|
solution_link_line(stdout, solution_path, self.app_state.emit_file_links())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
stdout.write_all(
|
stdout.write_all(
|
||||||
@@ -252,7 +252,7 @@ impl<'a> WatchState<'a> {
|
|||||||
stdout.write_all(b"\nCurrent exercise: ")?;
|
stdout.write_all(b"\nCurrent exercise: ")?;
|
||||||
self.app_state
|
self.app_state
|
||||||
.current_exercise()
|
.current_exercise()
|
||||||
.terminal_file_link(stdout)?;
|
.terminal_file_link(stdout, self.app_state.emit_file_links())?;
|
||||||
stdout.write_all(b"\n\n")?;
|
stdout.write_all(b"\n\n")?;
|
||||||
|
|
||||||
self.show_prompt(stdout)?;
|
self.show_prompt(stdout)?;
|
||||||
|
Reference in New Issue
Block a user