diff --git a/Cargo.lock b/Cargo.lock index ea216eb..71b8662 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2985,7 +2985,7 @@ dependencies = [ [[package]] name = "television" -version = "0.7.0" +version = "0.7.1" dependencies = [ "better-panic", "clap", @@ -3018,7 +3018,7 @@ dependencies = [ [[package]] name = "television-channels" -version = "0.0.11" +version = "0.0.12" dependencies = [ "clap", "color-eyre", @@ -3039,7 +3039,7 @@ dependencies = [ [[package]] name = "television-derive" -version = "0.0.11" +version = "0.0.12" dependencies = [ "proc-macro2", "quote", @@ -3048,7 +3048,7 @@ dependencies = [ [[package]] name = "television-fuzzy" -version = "0.0.11" +version = "0.0.12" dependencies = [ "nucleo", "parking_lot", @@ -3056,7 +3056,7 @@ dependencies = [ [[package]] name = "television-previewers" -version = "0.0.11" +version = "0.0.12" dependencies = [ "color-eyre", "devicons", @@ -3077,7 +3077,7 @@ dependencies = [ [[package]] name = "television-screen" -version = "0.0.11" +version = "0.0.12" dependencies = [ "color-eyre", "ratatui", @@ -3090,7 +3090,7 @@ dependencies = [ [[package]] name = "television-utils" -version = "0.0.11" +version = "0.0.12" dependencies = [ "bat", "color-eyre", diff --git a/Cargo.toml b/Cargo.toml index d55f56a..7d5ca22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "television" -version = "0.7.0" +version = "0.7.1" edition = "2021" description = "The revolution will be televised." license = "MIT" @@ -56,12 +56,12 @@ name = "tv" [dependencies] # workspace dependencies -television-fuzzy = { path = "crates/television-fuzzy", version = "0.0.11" } -television-derive = { path = "crates/television-derive", version = "0.0.11" } -television-screen = { path = "crates/television-screen", version = "0.0.11" } -television-channels = { path = "crates/television-channels", version = "0.0.11" } -television-previewers = { path = "crates/television-previewers", version = "0.0.11" } -television-utils = { path = "crates/television-utils", version = "0.0.11" } +television-fuzzy = { path = "crates/television-fuzzy", version = "0.0.12" } +television-derive = { path = "crates/television-derive", version = "0.0.12" } +television-screen = { path = "crates/television-screen", version = "0.0.12" } +television-channels = { path = "crates/television-channels", version = "0.0.12" } +television-previewers = { path = "crates/television-previewers", version = "0.0.12" } +television-utils = { path = "crates/television-utils", version = "0.0.12" } # external dependencies better-panic = "0.3.0" diff --git a/crates/television-channels/Cargo.toml b/crates/television-channels/Cargo.toml index b817436..b6aac52 100644 --- a/crates/television-channels/Cargo.toml +++ b/crates/television-channels/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "television-channels" -version = "0.0.11" +version = "0.0.12" description.workspace = true authors.workspace = true repository.workspace = true @@ -13,9 +13,9 @@ edition.workspace = true rust-version.workspace = true [dependencies] -television-fuzzy = { path = "../television-fuzzy", version = "0.0.11" } -television-utils = { path = "../television-utils", version = "0.0.11" } -television-derive = { path = "../television-derive", version = "0.0.11" } +television-fuzzy = { path = "../television-fuzzy", version = "0.0.12" } +television-utils = { path = "../television-utils", version = "0.0.12" } +television-derive = { path = "../television-derive", version = "0.0.12" } devicons = "0.6.11" tracing = "0.1.40" eyre = "0.6.12" diff --git a/crates/television-channels/src/channels.rs b/crates/television-channels/src/channels.rs index 7c25b2d..f0b2361 100644 --- a/crates/television-channels/src/channels.rs +++ b/crates/television-channels/src/channels.rs @@ -4,6 +4,7 @@ use television_derive::{Broadcast, ToCliChannel, ToUnitChannel}; mod alias; mod cable; +mod dirs; mod env; mod files; mod git_repos; @@ -120,6 +121,10 @@ pub enum TelevisionChannel { /// /// This channel allows to search through git repositories. GitRepos(git_repos::Channel), + /// The dirs channel. + /// + /// This channel allows to search through directories. + Dirs(dirs::Channel), /// The text channel. /// /// This channel allows to search through the contents of text files. @@ -170,6 +175,9 @@ macro_rules! variant_to_module { (Text) => { text::Channel }; + (Dirs) => { + dirs::Channel + }; (GitRepos) => { git_repos::Channel }; @@ -279,5 +287,6 @@ macro_rules! define_transitions { define_transitions! { Text => [Files, Text], Files => [Files, Text], - GitRepos => [Files, Text], + Dirs => [Files, Text, Dirs], + GitRepos => [Files, Text, Dirs], } diff --git a/crates/television-channels/src/channels/dirs.rs b/crates/television-channels/src/channels/dirs.rs new file mode 100644 index 0000000..01b657c --- /dev/null +++ b/crates/television-channels/src/channels/dirs.rs @@ -0,0 +1,159 @@ +use crate::channels::{OnAir, TelevisionChannel}; +use crate::entry::{Entry, PreviewCommand, PreviewType}; +use devicons::FileIcon; +use std::collections::HashSet; +use std::path::PathBuf; +use television_fuzzy::matcher::{config::Config, injector::Injector, Matcher}; +use television_utils::files::{walk_builder, DEFAULT_NUM_THREADS}; + +pub struct Channel { + matcher: Matcher, + crawl_handle: tokio::task::JoinHandle<()>, + // PERF: cache results (to make deleting characters smoother) with + // a shallow stack of sub-patterns as keys (e.g. "a", "ab", "abc") +} + +impl Channel { + pub fn new(paths: Vec) -> Self { + let matcher = Matcher::new(Config::default().match_paths(true)); + // start loading files in the background + let crawl_handle = tokio::spawn(load_dirs(paths, matcher.injector())); + Channel { + matcher, + crawl_handle, + } + } +} + +impl Default for Channel { + fn default() -> Self { + Self::new(vec![std::env::current_dir().unwrap()]) + } +} + +impl From<&mut TelevisionChannel> for Channel { + fn from(value: &mut TelevisionChannel) -> Self { + match value { + c @ TelevisionChannel::GitRepos(_) => { + let entries = c.results(c.result_count(), 0); + Self::new( + entries + .iter() + .map(|entry| PathBuf::from(entry.name.clone())) + .collect(), + ) + } + c @ TelevisionChannel::Dirs(_) => { + let entries = c.results(c.result_count(), 0); + Self::new( + entries + .iter() + .map(|entry| PathBuf::from(&entry.name)) + .collect::>() + .into_iter() + .collect(), + ) + } + _ => unreachable!(), + } + } +} + +#[cfg(unix)] +const PREVIEW_COMMAND: &str = "ls -la --color=always {}"; + +#[cfg(windows)] +const PREVIEW_COMMAND: &str = "dir /Q {}"; + +impl OnAir for Channel { + fn find(&mut self, pattern: &str) { + self.matcher.find(pattern); + } + + fn results(&mut self, num_entries: u32, offset: u32) -> Vec { + self.matcher.tick(); + self.matcher + .results(num_entries, offset) + .into_iter() + .map(|item| { + let path = item.matched_string; + Entry::new( + path.clone(), + PreviewType::Command(PreviewCommand::new( + PREVIEW_COMMAND, + " ", + )), + ) + .with_name_match_ranges(item.match_indices) + .with_icon(FileIcon::from(&path)) + }) + .collect() + } + + fn get_result(&self, index: u32) -> Option { + self.matcher.get_result(index).map(|item| { + let path = item.matched_string; + Entry::new( + path.clone(), + PreviewType::Command(PreviewCommand::new( + PREVIEW_COMMAND, + " ", + )), + ) + .with_icon(FileIcon::from(&path)) + }) + } + + fn result_count(&self) -> u32 { + self.matcher.matched_item_count + } + + fn total_count(&self) -> u32 { + self.matcher.total_item_count + } + + fn running(&self) -> bool { + self.matcher.status.running + } + + fn shutdown(&self) { + self.crawl_handle.abort(); + } +} + +#[allow(clippy::unused_async)] +async fn load_dirs(paths: Vec, injector: Injector) { + if paths.is_empty() { + return; + } + let current_dir = std::env::current_dir().unwrap(); + let mut builder = + walk_builder(&paths[0], *DEFAULT_NUM_THREADS, None, None); + paths[1..].iter().for_each(|path| { + builder.add(path); + }); + let walker = builder.build_parallel(); + + walker.run(|| { + let injector = injector.clone(); + let current_dir = current_dir.clone(); + Box::new(move |result| { + if let Ok(entry) = result { + if entry.file_type().unwrap().is_dir() { + let dir_path = &entry + .path() + .strip_prefix(¤t_dir) + .unwrap_or(entry.path()) + .to_string_lossy(); + if dir_path == "" { + return ignore::WalkState::Continue; + } + let () = injector.push(dir_path.to_string(), |e, cols| { + cols[0] = e.clone().into(); + }); + } + } + ignore::WalkState::Continue + }) + }); +} diff --git a/crates/television-channels/src/channels/files.rs b/crates/television-channels/src/channels/files.rs index e2e520e..ff76a32 100644 --- a/crates/television-channels/src/channels/files.rs +++ b/crates/television-channels/src/channels/files.rs @@ -63,6 +63,17 @@ impl From<&mut TelevisionChannel> for Channel { .collect(), ) } + c @ TelevisionChannel::Dirs(_) => { + let entries = c.results(c.result_count(), 0); + Self::new( + entries + .iter() + .map(|entry| PathBuf::from(&entry.name)) + .collect::>() + .into_iter() + .collect(), + ) + } _ => unreachable!(), } } diff --git a/crates/television-channels/src/channels/text.rs b/crates/television-channels/src/channels/text.rs index f05b05b..14ecda0 100644 --- a/crates/television-channels/src/channels/text.rs +++ b/crates/television-channels/src/channels/text.rs @@ -149,6 +149,15 @@ impl From<&mut TelevisionChannel> for Channel { let entries = c.results(c.result_count(), 0); Self::from_text_entries(entries) } + c @ TelevisionChannel::Dirs(_) => { + let entries = c.results(c.result_count(), 0); + Self::new( + entries + .iter() + .map(|entry| PathBuf::from(&entry.name)) + .collect(), + ) + } _ => unreachable!(), } } diff --git a/crates/television-derive/Cargo.toml b/crates/television-derive/Cargo.toml index 4036f83..2047e3e 100644 --- a/crates/television-derive/Cargo.toml +++ b/crates/television-derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "television-derive" -version = "0.0.11" +version = "0.0.12" description.workspace = true authors.workspace = true repository.workspace = true diff --git a/crates/television-fuzzy/Cargo.toml b/crates/television-fuzzy/Cargo.toml index 57af86c..8e17f50 100644 --- a/crates/television-fuzzy/Cargo.toml +++ b/crates/television-fuzzy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "television-fuzzy" -version = "0.0.11" +version = "0.0.12" description.workspace = true authors.workspace = true repository.workspace = true diff --git a/crates/television-previewers/Cargo.toml b/crates/television-previewers/Cargo.toml index 7741552..9f945b5 100644 --- a/crates/television-previewers/Cargo.toml +++ b/crates/television-previewers/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "television-previewers" -version = "0.0.11" +version = "0.0.12" description.workspace = true authors.workspace = true repository.workspace = true @@ -14,8 +14,8 @@ rust-version.workspace = true [dependencies] syntect = "5.2.0" -television-channels = { path = "../television-channels", version = "0.0.11" } -television-utils = { path = "../television-utils", version = "0.0.11" } +television-channels = { path = "../television-channels", version = "0.0.12" } +television-utils = { path = "../television-utils", version = "0.0.12" } tracing = "0.1.40" parking_lot = "0.12.3" tokio = "1.41.1" diff --git a/crates/television-screen/Cargo.toml b/crates/television-screen/Cargo.toml index d4ad882..51e4b80 100644 --- a/crates/television-screen/Cargo.toml +++ b/crates/television-screen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "television-screen" -version = "0.0.11" +version = "0.0.12" description.workspace = true authors.workspace = true repository.workspace = true @@ -15,8 +15,8 @@ rust-version.workspace = true [dependencies] ratatui = "0.29.0" serde = "1.0.215" -television-utils = { path = "../television-utils", version = "0.0.11" } -television-channels = { path = "../television-channels", version = "0.0.11" } -television-previewers = { path = "../television-previewers", version = "0.0.11" } +television-utils = { path = "../television-utils", version = "0.0.12" } +television-channels = { path = "../television-channels", version = "0.0.12" } +television-previewers = { path = "../television-previewers", version = "0.0.12" } color-eyre = "0.6.3" syntect = "5.2.0" diff --git a/crates/television-utils/Cargo.toml b/crates/television-utils/Cargo.toml index 487e4a8..ea5a15d 100644 --- a/crates/television-utils/Cargo.toml +++ b/crates/television-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "television-utils" -version = "0.0.11" +version = "0.0.12" description.workspace = true authors.workspace = true repository.workspace = true