mirror of https://github.com/sharkdp/fd.git
Highlight matches, see #46
This commit is contained in:
parent
8073635177
commit
8b175c1146
70
src/main.rs
70
src/main.rs
|
@ -112,7 +112,11 @@ static ROOT_DIR : &'static str = "/";
|
||||||
static PARENT_DIR : &'static str = "..";
|
static PARENT_DIR : &'static str = "..";
|
||||||
|
|
||||||
/// Print a search result to the console.
|
/// Print a search result to the console.
|
||||||
fn print_entry(base: &Path, entry: &PathBuf, config: &FdOptions) {
|
fn print_entry(base: &Path,
|
||||||
|
entry: &PathBuf,
|
||||||
|
match_start: usize,
|
||||||
|
match_end: usize,
|
||||||
|
config: &FdOptions) {
|
||||||
let path_full = base.join(entry);
|
let path_full = base.join(entry);
|
||||||
|
|
||||||
let path_str = entry.to_string_lossy();
|
let path_str = entry.to_string_lossy();
|
||||||
|
@ -138,6 +142,9 @@ fn print_entry(base: &Path, entry: &PathBuf, config: &FdOptions) {
|
||||||
print!("{}", ls_colors.directory.paint(ROOT_DIR));
|
print!("{}", ls_colors.directory.paint(ROOT_DIR));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut output_length = 0;
|
||||||
|
let mut within_match = false;
|
||||||
|
|
||||||
// Traverse the path and colorize each component
|
// Traverse the path and colorize each component
|
||||||
for component in entry.components() {
|
for component in entry.components() {
|
||||||
let comp_str = match component {
|
let comp_str = match component {
|
||||||
|
@ -178,11 +185,48 @@ fn print_entry(base: &Path, entry: &PathBuf, config: &FdOptions) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
write!(handle, "{}", style.paint(comp_str)).ok();
|
let comp_len = comp_str.len();
|
||||||
|
if match_start >= output_length && match_start < output_length + comp_len {
|
||||||
|
let (first, second) = comp_str.split_at(match_start - output_length);
|
||||||
|
write!(handle, "{}", style.paint(first)).ok();
|
||||||
|
if match_end < output_length + comp_len {
|
||||||
|
let (second_, third_) = second.split_at(match_end - match_start);
|
||||||
|
write!(handle, "{}", style.underline().paint(second_)).ok();
|
||||||
|
write!(handle, "{}", style.paint(third_)).ok();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
write!(handle, "{}", style.underline().paint(second)).ok();
|
||||||
|
within_match = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if within_match {
|
||||||
|
if match_end >= output_length && match_end < output_length + comp_len {
|
||||||
|
let (first, second) = comp_str.split_at(match_end - output_length);
|
||||||
|
write!(handle, "{}", style.underline().paint(first)).ok();
|
||||||
|
write!(handle, "{}", style.paint(second)).ok();
|
||||||
|
within_match = false;
|
||||||
|
} else {
|
||||||
|
write!(handle, "{}", style.underline().paint(comp_str)).ok();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
write!(handle, "{}", style.paint(comp_str)).ok();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
output_length += comp_len;
|
||||||
|
|
||||||
|
if match_end == output_length {
|
||||||
|
within_match = false;
|
||||||
|
}
|
||||||
|
|
||||||
if is_directory && component_path != path_full {
|
if is_directory && component_path != path_full {
|
||||||
let sep = std::path::MAIN_SEPARATOR.to_string();
|
let sep = std::path::MAIN_SEPARATOR.to_string();
|
||||||
write!(handle, "{}", style.paint(sep)).ok();
|
output_length += sep.len();
|
||||||
|
if within_match {
|
||||||
|
write!(handle, "{}", style.underline().paint(sep)).ok();
|
||||||
|
} else {
|
||||||
|
write!(handle, "{}", style.paint(sep)).ok();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +256,7 @@ fn print_entry(base: &Path, entry: &PathBuf, config: &FdOptions) {
|
||||||
|
|
||||||
/// Recursively scan the given search path and search for files / pathnames matching the pattern.
|
/// Recursively scan the given search path and search for files / pathnames matching the pattern.
|
||||||
fn scan(root: &Path, pattern: Arc<Regex>, base: &Path, config: Arc<FdOptions>) {
|
fn scan(root: &Path, pattern: Arc<Regex>, base: &Path, config: Arc<FdOptions>) {
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel::<(PathBuf, usize, usize)>();
|
||||||
|
|
||||||
let walker = WalkBuilder::new(root)
|
let walker = WalkBuilder::new(root)
|
||||||
.hidden(config.ignore_hidden)
|
.hidden(config.ignore_hidden)
|
||||||
|
@ -241,16 +285,16 @@ fn scan(root: &Path, pattern: Arc<Regex>, base: &Path, config: Arc<FdOptions>) {
|
||||||
let max_buffer_time = rx_config.max_buffer_time
|
let max_buffer_time = rx_config.max_buffer_time
|
||||||
.unwrap_or_else(|| time::Duration::from_millis(100));
|
.unwrap_or_else(|| time::Duration::from_millis(100));
|
||||||
|
|
||||||
for value in rx {
|
for (path, match_start, match_end) in rx {
|
||||||
match mode {
|
match mode {
|
||||||
ReceiverMode::Buffering => {
|
ReceiverMode::Buffering => {
|
||||||
buffer.push(value);
|
buffer.push((path, match_start, match_end));
|
||||||
|
|
||||||
// Have we reached the maximum time?
|
// Have we reached the maximum time?
|
||||||
if time::Instant::now() - start > max_buffer_time {
|
if time::Instant::now() - start > max_buffer_time {
|
||||||
// Flush the buffer
|
// Flush the buffer
|
||||||
for v in &buffer {
|
for &(ref p, m_s, m_e) in &buffer {
|
||||||
print_entry(&rx_base, v, &rx_config);
|
print_entry(&rx_base, &p, m_s, m_e, &rx_config);
|
||||||
}
|
}
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
|
|
||||||
|
@ -259,7 +303,7 @@ fn scan(root: &Path, pattern: Arc<Regex>, base: &Path, config: Arc<FdOptions>) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ReceiverMode::Streaming => {
|
ReceiverMode::Streaming => {
|
||||||
print_entry(&rx_base, &value, &rx_config);
|
print_entry(&rx_base, &path, match_start, match_end, &rx_config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -267,9 +311,9 @@ fn scan(root: &Path, pattern: Arc<Regex>, base: &Path, config: Arc<FdOptions>) {
|
||||||
// If we have finished fast enough (faster than max_buffer_time), we haven't streamed
|
// If we have finished fast enough (faster than max_buffer_time), we haven't streamed
|
||||||
// anything to the console, yet. In this case, sort the results and print them:
|
// anything to the console, yet. In this case, sort the results and print them:
|
||||||
if !buffer.is_empty() {
|
if !buffer.is_empty() {
|
||||||
buffer.sort();
|
buffer.sort_unstable_by_key(|t| t.0.to_owned());
|
||||||
for value in buffer {
|
for (path, match_start, match_end) in buffer {
|
||||||
print_entry(&rx_base, &value, &rx_config);
|
print_entry(&rx_base, &path, match_start, match_end, &rx_config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -318,7 +362,7 @@ fn scan(root: &Path, pattern: Arc<Regex>, base: &Path, config: Arc<FdOptions>) {
|
||||||
if let Some(search_str) = search_str_o {
|
if let Some(search_str) = search_str_o {
|
||||||
// TODO: take care of the unwrap call
|
// TODO: take care of the unwrap call
|
||||||
pattern.find(&*search_str)
|
pattern.find(&*search_str)
|
||||||
.map(|_| tx_thread.send(path_rel_buf.to_owned()).unwrap());
|
.map(|m| tx_thread.send((path_rel_buf.to_owned(), m.start(), m.end())).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
ignore::WalkState::Continue
|
ignore::WalkState::Continue
|
||||||
|
|
Loading…
Reference in New Issue