Skip to content

Commit

Permalink
start implementing a file channel
Browse files Browse the repository at this point in the history
  • Loading branch information
alexpasmantier committed Sep 23, 2024
1 parent b326e0b commit 4292573
Show file tree
Hide file tree
Showing 19 changed files with 347 additions and 84 deletions.
55 changes: 55 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ directories = "5.0.1"
futures = "0.3.30"
fuzzy-matcher = "0.3.7"
human-panic = "2.0.1"
ignore = "0.4.23"
json5 = "0.4.1"
lazy_static = "1.5.0"
libc = "0.2.158"
Expand Down
24 changes: 8 additions & 16 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
- separate event loop from tui
- translate events into actions inside the event handler loop
- have a dedicated rendering loop that can own the tui
-




## feature ideas
- environment variables
- aliases
- shell history
- grep (maybe also inside pdfs and other files (see rga))
- fd
- recent directories
- git
- makefile commands
- [x] environment variables
- [ ] aliases
- [ ] shell history
- [ ] grep (maybe also inside pdfs and other files (see rga))
- [ ] fd
- [ ] recent directories
- [ ] git
- [ ] makefile commands
-
1 change: 1 addition & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::config::{get_config_dir, get_data_dir};
pub enum UnitTvChannel {
#[default]
ENV,
FILES,
OTHER,
}

Expand Down
2 changes: 1 addition & 1 deletion src/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ use tokio::sync::mpsc::UnboundedSender;

use crate::{action::Action, config::Config};

pub mod channels;
mod finders;
pub mod fps;
pub mod home;
mod input;
mod pickers;
mod previewers;
mod sorters;
pub mod television;
mod utils;

Expand Down
49 changes: 49 additions & 0 deletions src/components/channels.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use color_eyre::Result;

use crate::cli::UnitTvChannel;
use crate::components::finders::Entry;
use crate::components::pickers::{self, Picker};
use crate::components::previewers;

pub enum TvChannel {
Env(pickers::env::EnvVarPicker),
Files(pickers::files::FilePicker),
}

impl TvChannel {
pub fn load_entries(&mut self, pattern: &str) -> Result<()> {
match self {
TvChannel::Env(picker) => picker.load_entries(pattern),
TvChannel::Files(picker) => picker.load_entries(pattern),
}
}

pub fn entries(&self) -> &Vec<Entry> {
match self {
TvChannel::Env(picker) => picker.entries(),
TvChannel::Files(picker) => picker.entries(),
}
}

pub fn clear(&mut self) {
match self {
TvChannel::Env(picker) => picker.clear(),
TvChannel::Files(picker) => picker.clear(),
}
}

pub fn get_preview(&mut self, entry: &Entry) -> previewers::Preview {
match self {
TvChannel::Env(picker) => picker.get_preview(entry),
TvChannel::Files(picker) => picker.get_preview(entry),
}
}
}

pub fn get_tv_channel(channel: UnitTvChannel) -> TvChannel {
match channel {
UnitTvChannel::ENV => TvChannel::Env(pickers::env::EnvVarPicker::new()),
UnitTvChannel::FILES => TvChannel::Files(pickers::files::FilePicker::new()),
_ => unimplemented!(),
}
}
13 changes: 12 additions & 1 deletion src/components/finders.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
use rust_devicons::FileIcon;

mod env;
mod files;

// finder types
pub use env::EnvVarFinder;
use rust_devicons::FileIcon;
pub use files::FileFinder;

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Entry {
pub name: String,
display_name: Option<String>,
pub preview: Option<String>,
pub score: i64,
pub name_match_ranges: Option<Vec<(usize, usize)>>,
Expand All @@ -15,8 +19,15 @@ pub struct Entry {
pub line_number: Option<usize>,
}

impl Entry {
pub fn display_name(&self) -> &str {
self.display_name.as_ref().unwrap_or(&self.name)
}
}

pub const ENTRY_PLACEHOLDER: Entry = Entry {
name: String::new(),
display_name: None,
preview: None,
score: 0,
name_match_ranges: None,
Expand Down
3 changes: 2 additions & 1 deletion src/components/finders/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ use std::{cmp::max, collections::HashMap, env::vars, path::Path};

use fuzzy_matcher::skim::SkimMatcherV2;
use rust_devicons::{icon_for_file, File, FileIcon};
use tracing::info;

use crate::components::finders::{Entry, Finder};

Expand Down Expand Up @@ -45,6 +44,7 @@ impl Finder for EnvVarFinder {
if pattern.is_empty() {
results.push(Entry {
name: env_var.name.clone(),
display_name: None,
preview: Some(env_var.value.clone()),
score: 0,
name_match_ranges: None,
Expand Down Expand Up @@ -73,6 +73,7 @@ impl Finder for EnvVarFinder {
if preview_match_ranges.is_some() || name_match_ranges.is_some() {
results.push(Entry {
name: env_var.name.clone(),
display_name: None,
preview: Some(env_var.value.clone()),
score: final_score,
name_match_ranges,
Expand Down
101 changes: 101 additions & 0 deletions src/components/finders/files.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use std::{
collections::HashMap,
path::{Path, PathBuf},
};

use fuzzy_matcher::skim::SkimMatcherV2;
use ignore::{types::TypesBuilder, WalkBuilder};
use tracing::info;

use crate::{
components::finders::{Entry, Finder},
config::default_num_threads,
};

pub struct FileFinder {
current_directory: PathBuf,
files: Vec<PathBuf>,
matcher: SkimMatcherV2,
cache: HashMap<String, Vec<Entry>>,
}

impl FileFinder {
pub fn new() -> Self {
let files = load_files(&std::env::current_dir().unwrap());
FileFinder {
current_directory: std::env::current_dir().unwrap(),
files,
matcher: SkimMatcherV2::default(),
cache: HashMap::new(),
}
}
}

impl Finder for FileFinder {
fn find(&mut self, pattern: &str) -> impl Iterator<Item = Entry> {
let mut results: Vec<Entry> = Vec::new();
// try to get from cache
if let Some(entries) = self.cache.get(pattern) {
results.extend(entries.iter().cloned());
} else {
for file in &self.files {
let file_name = file.file_name().unwrap().to_string_lossy().to_string();
if !pattern.is_empty() {
if let Some((score, indices)) = self.matcher.fuzzy(&file_name, pattern, true) {
results.push(Entry {
name: file_name.clone(),
display_name: None,
preview: None,
score,
name_match_ranges: Some(indices.iter().map(|i| (*i, *i + 1)).collect()),
preview_match_ranges: None,
icon: None,
line_number: None,
});
}
}
}
self.cache.insert(pattern.to_string(), results.clone());
}
results.into_iter()
}
}

const DEFAULT_RECV_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(250);

fn load_files(path: &Path) -> Vec<PathBuf> {
let (tx, rx) = std::sync::mpsc::channel();
let walker = walk_builder(path, default_num_threads().into()).build_parallel();
walker.run(|| {
let tx = tx.clone();
Box::new(move |result| {
if let Ok(entry) = result {
info!("found file: {:?}", entry.path());
if entry.file_type().unwrap().is_file() {
tx.send(entry.path().to_path_buf()).unwrap();
}
ignore::WalkState::Continue
} else {
ignore::WalkState::Continue
}
})
});

let mut files = Vec::new();
while let Ok(file) = rx.recv_timeout(DEFAULT_RECV_TIMEOUT) {
files.push(file);
}
files
}

fn walk_builder(path: &Path, n_threads: usize) -> WalkBuilder {
let mut builder = WalkBuilder::new(path);

// ft-based filtering
let mut types_builder = TypesBuilder::new();
types_builder.add_defaults();
builder.types(types_builder.build().unwrap());

builder.threads(n_threads);
builder
}
2 changes: 0 additions & 2 deletions src/components/input/backend.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use crate::event::Key;

use super::{Input, InputRequest, StateChanged};
use ratatui::crossterm::event::{
Event as CrosstermEvent, KeyCode, KeyEvent, KeyEventKind, KeyModifiers,
Expand Down
Loading

0 comments on commit 4292573

Please sign in to comment.