Skip to content

Commit 0c36e52

Browse files
committed
Change --hyperlink to be an option instead of a flag
1 parent d8d2c37 commit 0c36e52

File tree

6 files changed

+79
-54
lines changed

6 files changed

+79
-54
lines changed

contrib/completion/_fd

+2-2
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ _fd() {
139139
always\:"always use colorized output"
140140
))'
141141

142-
'--hyperlink[add hyperlinks to output paths]'
142+
'--hyperlink=-[add hyperlinks to output paths]::when:(auto never always)'
143143

144144
+ '(threads)'
145145
{-j+,--threads=}'[set the number of threads for searching and executing]:number of threads'
@@ -164,7 +164,7 @@ _fd() {
164164
$no'(*)*--search-path=[set search path (instead of positional <path> arguments)]:directory:_files -/'
165165

166166
+ strip-cwd-prefix
167-
$no'(strip-cwd-prefix exec-cmds)--strip-cwd-prefix=[When to strip ./]:when:(always never auto)'
167+
$no'(strip-cwd-prefix exec-cmds)--strip-cwd-prefix=-[When to strip ./]::when:(always never auto)'
168168

169169
+ and
170170
'--and=[additional required search path]:pattern'

doc/fd.1

+15-3
Original file line numberDiff line numberDiff line change
@@ -277,10 +277,22 @@ Always colorize output.
277277
.RE
278278
.TP
279279
.B "\-\-hyperlink
280-
Specify that the output should use terminal escape codes to indicate a hyperlink to a
280+
Specify whether the output should use terminal escape codes to indicate a hyperlink to a
281281
file url pointing to the path.
282-
Such hyperlinks will only actually be included if color output would be used, since
283-
that is likely correlated with the output being used on a terminal.
282+
283+
The value can be auto, always, or never.
284+
285+
Currently, the default is "never", and if the option is used without an argument "auto" is
286+
used. In the future this may be changed to "auto" and "always".
287+
.RS
288+
.IP auto
289+
Only output hyperlinks if color is also enabled, as a proxy for whether terminal escape
290+
codes are acceptable.
291+
.IP never
292+
Never output hyperlink escapes.
293+
.IP always
294+
Always output hyperlink escapes, regardless of color settings.
295+
.RE
284296
.TP
285297
.BI "\-j, \-\-threads " num
286298
Set number of threads to use for searching & executing (default: number of available CPU cores).

src/cli.rs

+25-4
Original file line numberDiff line numberDiff line change
@@ -511,10 +511,21 @@ pub struct Opts {
511511

512512
/// Add a terminal hyperlink to a file:// url for each path in the output.
513513
///
514-
/// This doesn't do anything for options that don't use the defualt output such as
515-
/// --exec and --format.
516-
#[arg(long, alias = "hyper", help = "Add hyperlinks to output paths")]
517-
pub hyperlink: bool,
514+
/// Auto mode is used if no argument is given to this option.
515+
///
516+
/// This doesn't do anything for --exec and --exec-batch.
517+
#[arg(
518+
long,
519+
alias = "hyper",
520+
value_name = "when",
521+
require_equals = true,
522+
value_enum,
523+
default_value_t = HyperlinkWhen::Never,
524+
default_missing_value = "auto",
525+
num_args = 0..=1,
526+
help = "Add hyperlinks to output paths"
527+
)]
528+
pub hyperlink: HyperlinkWhen,
518529

519530
/// Set number of threads to use for searching & executing (default: number
520531
/// of available CPU cores)
@@ -802,6 +813,16 @@ pub enum StripCwdWhen {
802813
Never,
803814
}
804815

816+
#[derive(Copy, Clone, PartialEq, Eq, Debug, ValueEnum)]
817+
pub enum HyperlinkWhen {
818+
/// Use hyperlinks only if color is enabled
819+
Auto,
820+
/// Always use hyperlinks when printing file paths
821+
Always,
822+
/// Never use hyperlinks
823+
Never,
824+
}
825+
805826
// there isn't a derive api for getting grouped values yet,
806827
// so we have to use hand-rolled parsing for exec and exec-batch
807828
pub struct Exec {

src/main.rs

+7-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use globset::GlobBuilder;
2525
use lscolors::LsColors;
2626
use regex::bytes::{Regex, RegexBuilder, RegexSetBuilder};
2727

28-
use crate::cli::{ColorWhen, Opts};
28+
use crate::cli::{ColorWhen, HyperlinkWhen, Opts};
2929
use crate::config::Config;
3030
use crate::exec::CommandSet;
3131
use crate::exit_codes::ExitCode;
@@ -235,6 +235,11 @@ fn construct_config(mut opts: Opts, pattern_regexps: &[String]) -> Result<Config
235235
} else {
236236
None
237237
};
238+
let hyperlink = match opts.hyperlink {
239+
HyperlinkWhen::Always => true,
240+
HyperlinkWhen::Never => false,
241+
HyperlinkWhen::Auto => colored_output,
242+
};
238243
let command = extract_command(&mut opts, colored_output)?;
239244
let has_command = command.is_some();
240245

@@ -259,7 +264,7 @@ fn construct_config(mut opts: Opts, pattern_regexps: &[String]) -> Result<Config
259264
threads: opts.threads().get(),
260265
max_buffer_time: opts.max_buffer_time,
261266
ls_colors,
262-
hyperlink: opts.hyperlink,
267+
hyperlink,
263268
interactive_terminal,
264269
file_types: opts.filetype.as_ref().map(|values| {
265270
use crate::cli::FileType::*;

src/output.rs

+24-42
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ use lscolors::{Indicator, LsColors, Style};
55

66
use crate::config::Config;
77
use crate::dir_entry::DirEntry;
8-
use crate::error::print_error;
9-
use crate::exit_codes::ExitCode;
108
use crate::fmt::FormatTemplate;
119
use crate::hyperlink::PathUrl;
1210

@@ -15,24 +13,31 @@ fn replace_path_separator(path: &str, new_path_separator: &str) -> String {
1513
}
1614

1715
// TODO: this function is performance critical and can probably be optimized
18-
pub fn print_entry<W: Write>(stdout: &mut W, entry: &DirEntry, config: &Config) {
19-
// TODO: use format if supplied
20-
let r = if let Some(ref format) = config.format {
21-
print_entry_format(stdout, entry, config, format)
16+
pub fn print_entry<W: Write>(stdout: &mut W, entry: &DirEntry, config: &Config) -> io::Result<()> {
17+
let mut has_hyperlink = false;
18+
if config.hyperlink {
19+
if let Some(url) = PathUrl::new(entry.path()) {
20+
write!(stdout, "\x1B]8;;{}\x1B\\", url)?;
21+
has_hyperlink = true;
22+
}
23+
}
24+
25+
if let Some(ref format) = config.format {
26+
print_entry_format(stdout, entry, config, format)?;
2227
} else if let Some(ref ls_colors) = config.ls_colors {
23-
print_entry_colorized(stdout, entry, config, ls_colors)
28+
print_entry_colorized(stdout, entry, config, ls_colors)?;
2429
} else {
25-
print_entry_uncolorized(stdout, entry, config)
30+
print_entry_uncolorized(stdout, entry, config)?;
2631
};
2732

28-
if let Err(e) = r {
29-
if e.kind() == ::std::io::ErrorKind::BrokenPipe {
30-
// Exit gracefully in case of a broken pipe (e.g. 'fd ... | head -n 3').
31-
ExitCode::Success.exit();
32-
} else {
33-
print_error(format!("Could not write to output: {}", e));
34-
ExitCode::GeneralError.exit();
35-
}
33+
if has_hyperlink {
34+
write!(stdout, "\x1B]8;;\x1B\\")?;
35+
}
36+
37+
if config.null_separator {
38+
write!(stdout, "\0")
39+
} else {
40+
writeln!(stdout)
3641
}
3742
}
3843

@@ -66,13 +71,12 @@ fn print_entry_format<W: Write>(
6671
config: &Config,
6772
format: &FormatTemplate,
6873
) -> io::Result<()> {
69-
let separator = if config.null_separator { "\0" } else { "\n" };
7074
let output = format.generate(
7175
entry.stripped_path(config),
7276
config.path_separator.as_deref(),
7377
);
7478
// TODO: support writing raw bytes on unix?
75-
write!(stdout, "{}{}", output.to_string_lossy(), separator)
79+
write!(stdout, "{}", output.to_string_lossy())
7680
}
7781

7882
// TODO: this function is performance critical and can probably be optimized
@@ -84,17 +88,9 @@ fn print_entry_colorized<W: Write>(
8488
) -> io::Result<()> {
8589
// Split the path between the parent and the last component
8690
let mut offset = 0;
87-
let mut has_hyperlink = false;
8891
let path = entry.stripped_path(config);
8992
let path_str = path.to_string_lossy();
9093

91-
if config.hyperlink {
92-
if let Some(url) = PathUrl::new(entry.path()) {
93-
write!(stdout, "\x1B]8;;{}\x1B\\", url)?;
94-
has_hyperlink = true;
95-
}
96-
}
97-
9894
if let Some(parent) = path.parent() {
9995
offset = parent.to_string_lossy().len();
10096
for c in path_str[offset..].chars() {
@@ -132,16 +128,6 @@ fn print_entry_colorized<W: Write>(
132128
ls_colors.style_for_indicator(Indicator::Directory),
133129
)?;
134130

135-
if has_hyperlink {
136-
write!(stdout, "\x1B]8;;\x1B\\")?;
137-
}
138-
139-
if config.null_separator {
140-
write!(stdout, "\0")?;
141-
} else {
142-
writeln!(stdout)?;
143-
}
144-
145131
Ok(())
146132
}
147133

@@ -151,16 +137,14 @@ fn print_entry_uncolorized_base<W: Write>(
151137
entry: &DirEntry,
152138
config: &Config,
153139
) -> io::Result<()> {
154-
let separator = if config.null_separator { "\0" } else { "\n" };
155140
let path = entry.stripped_path(config);
156141

157142
let mut path_string = path.to_string_lossy();
158143
if let Some(ref separator) = config.path_separator {
159144
*path_string.to_mut() = replace_path_separator(&path_string, separator);
160145
}
161146
write!(stdout, "{}", path_string)?;
162-
print_trailing_slash(stdout, entry, config, None)?;
163-
write!(stdout, "{}", separator)
147+
print_trailing_slash(stdout, entry, config, None)
164148
}
165149

166150
#[cfg(not(unix))]
@@ -185,9 +169,7 @@ fn print_entry_uncolorized<W: Write>(
185169
print_entry_uncolorized_base(stdout, entry, config)
186170
} else {
187171
// Print path as raw bytes, allowing invalid UTF-8 filenames to be passed to other processes
188-
let separator = if config.null_separator { b"\0" } else { b"\n" };
189172
stdout.write_all(entry.stripped_path(config).as_os_str().as_bytes())?;
190-
print_trailing_slash(stdout, entry, config, None)?;
191-
stdout.write_all(separator)
173+
print_trailing_slash(stdout, entry, config, None)
192174
}
193175
}

src/walk.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,12 @@ impl<'a, W: Write> ReceiverBuffer<'a, W> {
250250

251251
/// Output a path.
252252
fn print(&mut self, entry: &DirEntry) -> Result<(), ExitCode> {
253-
output::print_entry(&mut self.stdout, entry, self.config);
253+
if let Err(e) = output::print_entry(&mut self.stdout, entry, self.config) {
254+
if e.kind() != ::std::io::ErrorKind::BrokenPipe {
255+
print_error(format!("Could not write to output: {}", e));
256+
return Err(ExitCode::GeneralError);
257+
}
258+
}
254259

255260
if self.interrupt_flag.load(Ordering::Relaxed) {
256261
// Ignore any errors on flush, because we're about to exit anyway

0 commit comments

Comments
 (0)