Skip to content

Commit 2d1d531

Browse files
committed
Fix file links in VS Code
1 parent a712e48 commit 2d1d531

File tree

7 files changed

+57
-42
lines changed

7 files changed

+57
-42
lines changed

src/app_state.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ pub struct AppState {
6060
file_buf: Vec<u8>,
6161
official_exercises: bool,
6262
cmd_runner: CmdRunner,
63-
// Running in VS Code.
64-
vs_code: bool,
63+
emit_file_links: bool,
6564
}
6665

6766
impl AppState {
@@ -181,7 +180,8 @@ impl AppState {
181180
file_buf,
182181
official_exercises: !Path::new("info.toml").exists(),
183182
cmd_runner,
184-
vs_code: env::var_os("TERM_PROGRAM").is_some_and(|v| v == "vscode"),
183+
// VS Code has its own file link handling
184+
emit_file_links: env::var_os("TERM_PROGRAM").is_none_or(|v| v != "vscode"),
185185
};
186186

187187
Ok((slf, state_file_status))
@@ -218,8 +218,8 @@ impl AppState {
218218
}
219219

220220
#[inline]
221-
pub fn vs_code(&self) -> bool {
222-
self.vs_code
221+
pub fn emit_file_links(&self) -> bool {
222+
self.emit_file_links
223223
}
224224

225225
// Write the state file.
@@ -621,7 +621,7 @@ mod tests {
621621
file_buf: Vec::new(),
622622
official_exercises: true,
623623
cmd_runner: CmdRunner::build().unwrap(),
624-
vs_code: false,
624+
emit_file_links: true,
625625
};
626626

627627
let mut assert = |done: [bool; 3], expected: [Option<usize>; 3]| {

src/exercise.rs

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,28 @@ use std::io::{self, StdoutLock, Write};
77

88
use crate::{
99
cmd::CmdRunner,
10-
term::{self, CountedWrite, terminal_file_link, write_ansi},
10+
term::{self, CountedWrite, file_path, terminal_file_link, write_ansi},
1111
};
1212

1313
/// The initial capacity of the output buffer.
1414
pub const OUTPUT_CAPACITY: usize = 1 << 14;
1515

16-
pub fn solution_link_line(stdout: &mut StdoutLock, solution_path: &str) -> io::Result<()> {
16+
pub fn solution_link_line(
17+
stdout: &mut StdoutLock,
18+
solution_path: &str,
19+
emit_file_links: bool,
20+
) -> io::Result<()> {
1721
stdout.queue(SetAttribute(Attribute::Bold))?;
1822
stdout.write_all(b"Solution")?;
1923
stdout.queue(ResetColor)?;
2024
stdout.write_all(b" for comparison: ")?;
21-
if let Some(canonical_path) = term::canonicalize(solution_path) {
22-
terminal_file_link(stdout, solution_path, &canonical_path, Color::Cyan)?;
23-
} else {
24-
stdout.write_all(solution_path.as_bytes())?;
25-
}
25+
file_path(stdout, Color::Cyan, |writer| {
26+
if emit_file_links && let Some(canonical_path) = term::canonicalize(solution_path) {
27+
terminal_file_link(writer, solution_path, &canonical_path)
28+
} else {
29+
writer.stdout().write_all(solution_path.as_bytes())
30+
}
31+
})?;
2632
stdout.write_all(b"\n")
2733
}
2834

@@ -72,12 +78,18 @@ pub struct Exercise {
7278
}
7379

7480
impl Exercise {
75-
pub fn terminal_file_link<'a>(&self, writer: &mut impl CountedWrite<'a>) -> io::Result<()> {
76-
if let Some(canonical_path) = self.canonical_path.as_deref() {
77-
return terminal_file_link(writer, self.path, canonical_path, Color::Blue);
78-
}
79-
80-
writer.write_str(self.path)
81+
pub fn terminal_file_link<'a>(
82+
&self,
83+
writer: &mut impl CountedWrite<'a>,
84+
emit_file_links: bool,
85+
) -> io::Result<()> {
86+
file_path(writer, Color::Blue, |writer| {
87+
if emit_file_links && let Some(canonical_path) = self.canonical_path.as_deref() {
88+
terminal_file_link(writer, self.path, canonical_path)
89+
} else {
90+
writer.write_str(self.path)
91+
}
92+
})
8193
}
8294
}
8395

src/list/state.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -186,13 +186,7 @@ impl<'a> ListState<'a> {
186186

187187
writer.write_ascii(&self.name_col_padding[exercise.name.len()..])?;
188188

189-
// The list links aren't shown correctly in VS Code on Windows.
190-
// But VS Code shows its own links anyway.
191-
if self.app_state.vs_code() {
192-
writer.write_str(exercise.path)?;
193-
} else {
194-
exercise.terminal_file_link(&mut writer)?;
195-
}
189+
exercise.terminal_file_link(&mut writer, self.app_state.emit_file_links())?;
196190

197191
writer.write_ascii(&self.path_col_padding[exercise.path.len()..])?;
198192

src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ fn main() -> Result<ExitCode> {
167167
}
168168
app_state
169169
.current_exercise()
170-
.terminal_file_link(&mut stdout)?;
170+
.terminal_file_link(&mut stdout, app_state.emit_file_links())?;
171171
stdout.write_all(b"\n")?;
172172

173173
return Ok(ExitCode::FAILURE);

src/run.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub fn run(app_state: &mut AppState) -> Result<ExitCode> {
2727
stdout.write_all(b"Ran ")?;
2828
app_state
2929
.current_exercise()
30-
.terminal_file_link(&mut stdout)?;
30+
.terminal_file_link(&mut stdout, app_state.emit_file_links())?;
3131
stdout.write_all(b" with errors\n")?;
3232

3333
return Ok(ExitCode::FAILURE);
@@ -41,7 +41,7 @@ pub fn run(app_state: &mut AppState) -> Result<ExitCode> {
4141

4242
if let Some(solution_path) = app_state.current_solution_path()? {
4343
stdout.write_all(b"\n")?;
44-
solution_link_line(&mut stdout, &solution_path)?;
44+
solution_link_line(&mut stdout, &solution_path, app_state.emit_file_links())?;
4545
stdout.write_all(b"\n")?;
4646
}
4747

@@ -50,7 +50,7 @@ pub fn run(app_state: &mut AppState) -> Result<ExitCode> {
5050
stdout.write_all(b"Next exercise: ")?;
5151
app_state
5252
.current_exercise()
53-
.terminal_file_link(&mut stdout)?;
53+
.terminal_file_link(&mut stdout, app_state.emit_file_links())?;
5454
stdout.write_all(b"\n")?;
5555
}
5656
ExercisesProgress::AllDone => (),

src/term.rs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -272,22 +272,18 @@ pub fn canonicalize(path: &str) -> Option<String> {
272272
})
273273
}
274274

275-
pub fn terminal_file_link<'a>(
276-
writer: &mut impl CountedWrite<'a>,
277-
path: &str,
278-
canonical_path: &str,
275+
pub fn file_path<'a, W: CountedWrite<'a>>(
276+
writer: &mut W,
279277
color: Color,
278+
f: impl FnOnce(&mut W) -> io::Result<()>,
280279
) -> io::Result<()> {
281280
writer
282281
.stdout()
283282
.queue(SetForegroundColor(color))?
284283
.queue(SetAttribute(Attribute::Underlined))?;
285-
writer.stdout().write_all(b"\x1b]8;;file://")?;
286-
writer.stdout().write_all(canonical_path.as_bytes())?;
287-
writer.stdout().write_all(b"\x1b\\")?;
288-
// Only this part is visible.
289-
writer.write_str(path)?;
290-
writer.stdout().write_all(b"\x1b]8;;\x1b\\")?;
284+
285+
f(writer)?;
286+
291287
writer
292288
.stdout()
293289
.queue(SetForegroundColor(Color::Reset))?
@@ -296,6 +292,19 @@ pub fn terminal_file_link<'a>(
296292
Ok(())
297293
}
298294

295+
pub fn terminal_file_link<'a>(
296+
writer: &mut impl CountedWrite<'a>,
297+
path: &str,
298+
canonical_path: &str,
299+
) -> io::Result<()> {
300+
writer.stdout().write_all(b"\x1b]8;;file://")?;
301+
writer.stdout().write_all(canonical_path.as_bytes())?;
302+
writer.stdout().write_all(b"\x1b\\")?;
303+
// Only this part is visible.
304+
writer.write_str(path)?;
305+
writer.stdout().write_all(b"\x1b]8;;\x1b\\")
306+
}
307+
299308
pub fn write_ansi(output: &mut Vec<u8>, command: impl Command) {
300309
struct FmtWriter<'a>(&'a mut Vec<u8>);
301310

src/watch/state.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ impl<'a> WatchState<'a> {
233233
stdout.write_all(b"\n")?;
234234

235235
if let DoneStatus::DoneWithSolution(solution_path) = &self.done_status {
236-
solution_link_line(stdout, solution_path)?;
236+
solution_link_line(stdout, solution_path, self.app_state.emit_file_links())?;
237237
}
238238

239239
stdout.write_all(
@@ -252,7 +252,7 @@ impl<'a> WatchState<'a> {
252252
stdout.write_all(b"\nCurrent exercise: ")?;
253253
self.app_state
254254
.current_exercise()
255-
.terminal_file_link(stdout)?;
255+
.terminal_file_link(stdout, self.app_state.emit_file_links())?;
256256
stdout.write_all(b"\n\n")?;
257257

258258
self.show_prompt(stdout)?;

0 commit comments

Comments
 (0)