summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrabite <rabite@posteo.de>2020-02-11 23:37:38 +0100
committerrabite <rabite@posteo.de>2020-02-11 23:46:14 +0100
commit74e43ee9659ed08891e5bd14b5be889d0bad5d7e (patch)
treeed2ccbc7a19431e6024718b3d4aa536b3d0d1c91
parent800592338d194841d927450b9466c4a425270404 (diff)
the fastest file manager in the galaxy
-rw-r--r--Cargo.lock23
-rw-r--r--Cargo.toml6
-rw-r--r--src/fail.rs10
-rw-r--r--src/file_browser.rs79
-rw-r--r--src/files.rs822
-rw-r--r--src/fscache.rs81
-rw-r--r--src/listview.rs290
-rw-r--r--src/preview.rs7
-rw-r--r--src/trait_ext.rs5
9 files changed, 740 insertions, 583 deletions
diff --git a/Cargo.lock b/Cargo.lock
index e3167a0..6884e32 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -35,6 +35,14 @@ dependencies = [
]
[[package]]
+name = "ansi_term"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "argon2rs"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -63,7 +71,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "async_value"
-version = "0.2.6"
+version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -580,7 +588,7 @@ name = "hunter"
version = "1.3.5"
dependencies = [
"alphanumeric-sort 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
- "async_value 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "async_value 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -597,7 +605,7 @@ dependencies = [
"itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
- "lscolors 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lscolors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
"mime_guess 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"notify 4.0.15 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -726,10 +734,10 @@ dependencies = [
[[package]]
name = "lscolors"
-version = "0.5.0"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1553,11 +1561,12 @@ dependencies = [
"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
"checksum alphanumeric-sort 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "f37ce94154d73f6961f87571a3ab7814e1608f373bd55a933e3e771b6dd59fc4"
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
+"checksum ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
"checksum argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392"
"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee"
"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
-"checksum async_value 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "35a62a04436ffd962e78a3301658eac900480d425ea36ec42553aadc572742be"
+"checksum async_value 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "82d81baa3badf6d557265cedafc6fc83b4b79567360bbadcfc5ea6eb934885ae"
"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
@@ -1625,7 +1634,7 @@ dependencies = [
"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
-"checksum lscolors 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9938fd8c379393454f73ec4c9c5b40f3d8332d80b25a29da05e41ee0ecbb559"
+"checksum lscolors 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea3b3414b2d015c4fd689815f2551797f3c2296bb241dd709c7da233ec7cba4b"
"checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084"
"checksum make-cmd 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8ca8afbe8af1785e09636acb5a41e08a765f5f0340568716c18a8700ba3c0d3"
"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
diff --git a/Cargo.toml b/Cargo.toml
index f43b1d8..e18845c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,8 +17,8 @@ default-run = "hunter"
termion = "1.5"
unicode-width = "0.1.5"
lazy_static = "1"
-alphanumeric-sort = "1.0.6"
-lscolors = { version = "0.5.0", features = [ "ansi_term" ] }
+alphanumeric-sort = "1.0.11"
+lscolors = { version = "0.6.0", features = [ "ansi_term" ] }
tree_magic = "0.2.1"
rayon = "1.3"
dirs-2 = "1.1.0"
@@ -32,7 +32,7 @@ parse-ansi = "0.1.6"
signal-notify = "0.1.3"
systemstat = "0.1.5"
mime_guess = "2.0"
-async_value = "0.2.6"
+async_value = "0.2.7"
osstrtools = "0.2"
pathbuftools = "0.1"
clap = "2.33"
diff --git a/src/fail.rs b/src/fail.rs
index 36a88d4..0f05455 100644
--- a/src/fail.rs
+++ b/src/fail.rs
@@ -103,7 +103,9 @@ pub enum HError {
#[fail(display = "{}", _0)]
KeyBind(KeyBindError),
#[fail(display = "FileBrowser needs to know about all tab's files to run exec!")]
- FileBrowserNeedTabFiles
+ FileBrowserNeedTabFiles,
+ #[fail(display = "{}", _0)]
+ FileError(crate::files::FileError)
}
impl HError {
@@ -425,3 +427,9 @@ impl From<ini::ini::Error> for KeyBindError {
KeyBindError::IniError(Arc::new(err))
}
}
+
+impl From<crate::files::FileError> for HError {
+ fn from(err: crate::files::FileError) -> Self {
+ HError::FileError(err)
+ }
+}
diff --git a/src/file_browser.rs b/src/file_browser.rs
index 8c99409..3a84ee1 100644
--- a/src/file_browser.rs
+++ b/src/file_browser.rs
@@ -197,7 +197,7 @@ impl Tabbable for TabView<FileBrowser> {
tab.left_async_widget_mut().map(|async_w| {
async_w.widget.on_ready(move |mut w, _| {
w.as_mut()
- .map(|mut w| {
+ .map(|w| {
if w.content.show_hidden != show_hidden {
w.content.show_hidden = show_hidden;
w.content.recalculate_len();
@@ -211,7 +211,7 @@ impl Tabbable for TabView<FileBrowser> {
tab.main_async_widget_mut().map(|async_w| {
async_w.widget.on_ready(move |mut w, _| {
w.as_mut()
- .map(|mut w| {
+ .map(|w| {
if w.content.show_hidden != show_hidden {
w.content.show_hidden = show_hidden;
w.content.recalculate_len();
@@ -262,11 +262,9 @@ impl FileBrowser {
let cache = fs_cache.clone();
let main_widget = AsyncWidget::new(&core, move |stale| {
- let dir = File::new_from_path(&main_path, None)?;
+ let dir = File::new_from_path(&main_path)?;
let source = FileSource::Path(dir);
ListView::builder(core_m, source)
- .meta_all()
- // .prerender()
.with_cache(cache)
.with_stale(stale.clone())
.build()
@@ -275,11 +273,9 @@ impl FileBrowser {
let cache = fs_cache.clone();
if let Some(left_path) = left_path {
let left_widget = AsyncWidget::new(&core_l.clone(), move |stale| {
- let dir = File::new_from_path(&left_path, None)?;
+ let dir = File::new_from_path(&left_path)?;
let source = FileSource::Path(dir);
ListView::builder(core_l, source)
- // .meta_all()
- // .prerender()
.with_cache(cache)
.with_stale(stale.clone())
.build()
@@ -311,7 +307,7 @@ impl FileBrowser {
columns.refresh().log();
- let cwd = File::new_from_path(&cwd, None).unwrap();
+ let cwd = File::new_from_path(&cwd).unwrap();
let proc_view = ProcView::new(&core);
let bookmarks = BMPopup::new(&core);
@@ -363,8 +359,6 @@ impl FileBrowser {
};
ListView::builder(core, source)
- .meta_all()
- // .prerender()
.with_cache(cache)
.with_stale(stale.clone())
.build()
@@ -380,7 +374,6 @@ impl FileBrowser {
};
ListView::builder(core, source)
- .prerender()
.with_cache(cache)
.with_stale(stale.clone())
.build()
@@ -479,6 +472,10 @@ impl FileBrowser {
}
pub fn main_widget_goto(&mut self, dir: &File) -> HResult<()> {
+ self.preview_widget_mut()
+ .map(|p| p.set_stale())
+ .ok();
+
let dir = dir.clone();
let cache = self.fs_cache.clone();
@@ -489,8 +486,6 @@ impl FileBrowser {
let main_async_widget = self.main_async_widget_mut()?;
main_async_widget.change_to(move |stale: &Stale, core| {
let view = ListView::builder(core, file_source)
- .meta_all()
- // .prerender()
.with_cache(cache)
.with_stale(stale.clone())
.build()?;
@@ -507,9 +502,7 @@ impl FileBrowser {
}).log();
}
- self.preview_widget_mut()
- .map(|p| p.set_stale())
- .ok();
+
Ok(())
}
@@ -527,8 +520,6 @@ impl FileBrowser {
let left_async_widget = self.left_async_widget_mut()?;
left_async_widget.change_to(move |stale, core| {
let view = ListView::builder(core, file_source)
- // .meta_all()
- // .prerender()
.with_cache(cache)
.with_stale(stale.clone())
.build()?;
@@ -559,8 +550,6 @@ impl FileBrowser {
self.main_async_widget_mut()?.change_to(move |stale, core| {
ListView::builder(core, file_source)
.select(main_selection)
- .meta_all()
- // .prerender()
.with_cache(cache)
.with_stale(stale.clone())
.build()
@@ -571,7 +560,6 @@ impl FileBrowser {
let cache = self.fs_cache.clone();
self.left_async_widget_mut()?.change_to(move |stale, core| {
ListView::builder(core, file_source)
- // .prerender()
.with_cache(cache)
.with_stale(stale.clone())
.build()
@@ -612,7 +600,7 @@ impl FileBrowser {
pub fn go_home(&mut self) -> HResult<()> {
let home = crate::paths::home_path().unwrap_or(PathBuf::from("~/"));
- let home = File::new_from_path(&home, None)?;
+ let home = File::new_from_path(&home)?;
self.main_widget_goto(&home)
}
@@ -649,7 +637,7 @@ impl FileBrowser {
pub fn goto_bookmark(&mut self) -> HResult<()> {
let path = self.get_boomark()?;
- let path = File::new_from_path(&PathBuf::from(path), None)?;
+ let path = File::new_from_path(&PathBuf::from(path))?;
self.main_widget_goto(&path)?;
Ok(())
}
@@ -700,23 +688,35 @@ impl FileBrowser {
let selection = self.cwd()?.clone();
- self.cwd.parent_as_file()
- .map(|dir| self.fs_cache
- .set_selection(dir.clone(), selection.clone())).log();
+ // Saves doing iteration to find file's position
+ if let Some(ref current_selection) = self.left_widget()?.current_item {
+ if current_selection.name == selection.name {
+ return Ok(());
+ }
+ }
+
self.left_widget_mut()?.select_file(&selection);
+ let selected_file = self.left_widget()?.selected_file();
+ self.cwd.parent_as_file()
+ .map(|dir| {
+ self.fs_cache
+ .set_selection(dir.clone(), selected_file.clone())
+ }).log();
+
+
Ok(())
}
pub fn take_main_files(&mut self) -> HResult<Files> {
- let mut w = self.main_widget_mut()?;
+ let w = self.main_widget_mut()?;
let files = std::mem::take(&mut w.content);
w.content.len = 0;
Ok(files)
}
pub fn take_left_files(&mut self) -> HResult<Files> {
- let mut w = self.left_widget_mut()?;
+ let w = self.left_widget_mut()?;
let files = std::mem::take(&mut w.content);
w.content.len = 0;
Ok(files)
@@ -879,7 +879,7 @@ impl FileBrowser {
let dir = self.core.minibuffer("cd")?;
let path = std::path::PathBuf::from(&dir);
- let dir = File::new_from_path(&path.canonicalize()?, None)?;
+ let dir = File::new_from_path(&path.canonicalize()?)?;
self.main_widget_goto(&dir)?;
Ok(())
@@ -931,11 +931,11 @@ impl FileBrowser {
let path = &paths[0];
if path.exists() {
if path.is_dir() {
- let dir = File::new_from_path(&path, None)?;
+ let dir = File::new_from_path(&path)?;
self.main_widget_goto(&dir).log();
} else if path.is_file() {
- let file = File::new_from_path(&path, None)?;
+ let file = File::new_from_path(&path)?;
let dir = file.parent_as_file()?;
self.main_widget_goto(&dir).log();
@@ -964,7 +964,7 @@ impl FileBrowser {
let dir_path = file_path.parent()?;
if self.cwd.path != dir_path {
- let file_dir = File::new_from_path(&dir_path, None);
+ let file_dir = File::new_from_path(&dir_path);
self.main_widget_goto_wait(&file_dir?).log();
}
@@ -1033,7 +1033,7 @@ impl FileBrowser {
if path.exists() {
if path.is_dir() {
- let dir = File::new_from_path(&path, None)?;
+ let dir = File::new_from_path(&path)?;
self.main_widget_goto(&dir).log();
}
else {
@@ -1231,12 +1231,15 @@ impl Widget for FileBrowser {
let file = self.selected_file()?;
let name = &file.name;
+ let fcolor = file.get_color();
+
let color = if file.is_dir() {
crate::term::highlight_color() }
- else if file.color.is_none() {
- crate::term::normal_color()
- } else {
- crate::term::from_lscolor(file.color.as_ref().unwrap())
+ else {
+ match fcolor {
+ Some(color) => color,
+ None => crate::term::normal_color()
+ }
};
let path = self.cwd.short_string();
diff --git a/src/files.rs b/src/files.rs
index de8dc29..43c603f 100644
--- a/src/files.rs
+++ b/src/files.rs
@@ -8,8 +8,10 @@ use std::sync::{Arc, Mutex, RwLock};
use std::sync::mpsc::Sender;
use std::hash::{Hash, Hasher};
use std::str::FromStr;
-use std::sync::atomic::{AtomicU32, Ordering};
+use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
+use failure;
+use failure::Fail;
use lscolors::LsColors;
use tree_magic;
use users::{get_current_username,
@@ -27,33 +29,61 @@ use pathbuftools::PathBufTools;
use async_value::{Async, Stale, StopIter};
use crate::fail::{HResult, HError, ErrorLog};
-use crate::dirty::{AsyncDirtyBit, DirtyBit, Dirtyable};
+use crate::dirty::{DirtyBit, Dirtyable};
use crate::widget::Events;
use crate::icon::Icons;
-use crate::fscache::FsEvent;
-
+use crate::fscache::{FsCache, FsEvent};
lazy_static! {
static ref COLORS: LsColors = LsColors::from_env().unwrap_or_default();
static ref TAGS: RwLock<(bool, Vec<PathBuf>)> = RwLock::new((false, vec![]));
static ref ICONS: Icons = Icons::new();
+ static ref IOPOOL: Mutex<Option<ThreadPool>> = Mutex::new(None);
+ static ref IOTICK: AtomicUsize = AtomicUsize::default();
+ static ref TICKING: AtomicBool = AtomicBool::new(false);
+}
+
+pub fn tick() {
+ IOTICK.fetch_add(1, Ordering::Relaxed);
+}
+
+pub fn get_tick() -> usize {
+ IOTICK.load(Ordering::Relaxed)
+}
+
+pub fn tick_str() -> &'static str {
+ // Using mod 5 for that nice nonlinear look
+ match get_tick() % 5 {
+ 0 => " ",
+ 1 => ". ",
+ 2 => ".. ",
+ _ => "..."
+ }
+}
+
+pub fn is_ticking() -> bool {
+ TICKING.load(Ordering::Acquire)
+}
+
+pub fn set_ticking(val: bool) {
+ TICKING.store(val, Ordering::Release);
+}
+
+#[derive(Fail, Debug, Clone)]
+pub enum FileError {
+ #[fail(display = "Metadata still pending!")]
+ MetaPending
}
-fn make_pool(sender: Option<Sender<Events>>) -> ThreadPool {
- let sender = Arc::new(Mutex::new(sender));
+pub fn get_pool() -> ThreadPool {
+ // Optimal number of threads depends on many things. This is a reasonable default.
+ const THREAD_NUM: usize = 8;
+
ThreadPoolBuilder::new()
- .num_threads(8)
- .exit_handler(move |thread_num| {
- if thread_num == 0 {
- if let Ok(lock) = sender.lock() {
- if let Some(sender) = lock.as_ref() {
- sender.send(Events::WidgetReady).ok();
- }
- }
- }
- })
+ .num_threads(THREAD_NUM)
+ .thread_name(|i| format!("hunter_iothread_{}", i))
.build()
- .expect("Failed to create thread pool")
+ .unwrap()
}
pub fn load_tags() -> HResult<()> {
@@ -100,22 +130,22 @@ pub fn tags_loaded() -> HResult<()> {
else { HError::tags_not_loaded() }
}
-
-#[derive(PartialEq, Eq, Hash, Clone, Debug)]
+#[derive(Derivative)]
+#[derivative(PartialEq, Eq, Hash, Clone, Debug)]
pub struct RefreshPackage {
pub new_files: Option<Vec<File>>,
- pub new_buffer: Option<Vec<String>>,
pub new_len: usize,
+ #[derivative(Debug="ignore")]
+ #[derivative(PartialEq="ignore")]
+ #[derivative(Hash="ignore")]
+ pub jobs: Vec<Job>
}
impl RefreshPackage {
- fn new(mut files: Files,
- old_buffer: Vec<String>,
- events: Vec<FsEvent>,
- render_fn: impl Fn(&File) -> String) -> RefreshPackage {
+ fn new(mut files: Files, events: Vec<FsEvent>) -> RefreshPackage {
use FsEvent::*;
// If there is only a placeholder at this point, remove it now
@@ -137,15 +167,6 @@ impl RefreshPackage {
.map(|(i, file)| (file, i))
.collect();
-
- // Need to know which line of the ListView buffer belongs to which file
- let list_pos_map: HashMap<&File, usize> = static_files
- .iter_files()
- .enumerate()
- .take_while(|&(i, _)| i < old_buffer.len())
- .map(|(i, file)| (file, i))
- .collect();
-
// Save new files to add them all at once later
let mut new_files = Vec::with_capacity(event_count);
@@ -155,23 +176,35 @@ impl RefreshPackage {
// Save deletions to delete them efficiently later
let mut deleted_files = HashSet::with_capacity(event_count);
- for event in events.into_iter() {
+ // Stores jobs to asynchronously fetch metadata
+ let mut jobs = Vec::with_capacity(event_count);
+
+ let cache = &files.cache.take().unwrap();
+
+ // Drop would set this stale after the function returns
+ let stale = files.stale.take().unwrap();
+
+
+ for event in events.into_iter().stop_stale(stale.clone()) {
match event {
Create(mut file) => {
- file.meta_sync().log();
+ let job = file.prepare_meta_job(cache);
+ job.map(|j| jobs.push(j));
new_files.push(file);
}
Change(file) => {
if let Some(&fpos) = file_pos_map.get(&file) {
- files.files[fpos].meta_sync().log();
+ let job = files.files[fpos].refresh_meta_job();
+ jobs.push(job);
changed_files.insert(file);
}
}
Rename(old, new) => {
if let Some(&fpos) = file_pos_map.get(&old) {
files.files[fpos].rename(&new.path).log();
- files.files[fpos].meta_sync().log();
- }
+ let job = files.files[fpos].refresh_meta_job();
+ jobs.push(job);
+ }
}
Remove(file) => {
if let Some(_) = file_pos_map.get(&file) {
@@ -181,6 +214,15 @@ impl RefreshPackage {
}
}
+ // Bail out without further processing
+ if stale.is_stale().unwrap_or(true) {
+ return RefreshPackage {
+ new_files: None,
+ new_len: 0,
+ jobs: jobs
+ }
+ }
+
if deleted_files.len() > 0 {
files.files.retain(|file| !deleted_files.contains(file));
}
@@ -192,41 +234,28 @@ impl RefreshPackage {
files.recalculate_len();
files.sort();
- // Prerender new buffer in current thread
- let mut old_buffer = old_buffer;
-
- let new_buffer = files.iter_files()
- .map(|file| {
- match list_pos_map.get(&file) {
- Some(&old_pos) =>
- match changed_files.contains(&file) {
- true => render_fn(&file),
- false => std::mem::take(&mut old_buffer[old_pos])
- }
- None => render_fn(&file)
- }
- }).collect();
-
// Need to unpack this to prevent issue with recursive Files type
- // Also, if no files remain add placeholder and set len
- let (files, new_len, new_buffer) = if files.len() > 0 {
- (files.files, files.len, new_buffer)
+ // Also, if no files remain add placeholder and set len
+ let (files, new_len) = if files.len() > 0 {
+ (std::mem::take(&mut files.files), files.len)
} else {
let placeholder = File::new_placeholder(&files.directory.path).unwrap();
- let buffer = vec![render_fn(&placeholder)];
files.files.push(placeholder);
- (files.files, 1, buffer)
+ (std::mem::take(&mut files.files), 1)
};
-
RefreshPackage {
new_files: Some(files),
- new_buffer: Some(new_buffer),
- new_len: new_len
+ new_len: new_len,
+ jobs: jobs
}
}
}
+// Tuple that stores path and "slots" to store metaadata in
+pub type Job = (PathBuf,
+ Option<Arc<RwLock<Option<Metadata>>>>,
+ Option<Arc<(AtomicBool, AtomicUsize)>>);
#[derive(Derivative)]
#[derivative(PartialEq, Eq, Hash, Clone, Debug)]
@@ -250,6 +279,18 @@ pub struct Files {
pub filter: Option<String>,
pub filter_selected: bool,
pub dirty: DirtyBit,
+ #[derivative(Debug="ignore")]
+ #[derivative(PartialEq="ignore")]
+ #[derivative(Hash="ignore")]
+ pub jobs: Vec<Job>,
+ #[derivative(Debug="ignore")]
+ #[derivative(PartialEq="ignore")]
+ #[derivative(Hash="ignore")]
+ pub cache: Option<FsCache>,
+ #[derivative(Debug="ignore")]
+ #[derivative(PartialEq="ignore")]
+ #[derivative(Hash="ignore")]
+ pub stale: Option<Stale>
}
impl Index<usize> for Files {
@@ -292,83 +333,173 @@ impl Default for Files {
filter: None,
filter_selected: false,
dirty: DirtyBit::new(),
+ jobs: vec![],
+ cache: None,
+ stale: None
}
}
}
+// Stop processing stuff when Files is dropped
+impl Drop for Files {
+ fn drop(&mut self) {
+ self.stale
+ .as_ref()
+ .map(|s| s.set_stale());
+ }
+}
+
impl Files {
- pub fn new_from_path(path: &Path) -> HResult<Files> {
- let direntries: Result<Vec<_>, _> = std::fs::read_dir(&path)?.collect();
- let dirty_meta = AsyncDirtyBit::new();
- let tags = &TAGS.read().ok()?.1;
+ pub fn new_from_path_cancellable(path: &Path, stale: Stale) -> HResult<Files> {
+ let direntries: Vec<std::fs::DirEntry> = std::fs::read_dir(&path)?
+ .stop_stale(stale.clone())
+ .collect::<Result<Vec<std::fs::DirEntry>, _>>()?;
- let files: Vec<_> = direntries?
- .iter()
- .map(|file| {
- let name = file.file_name();
- let name = name.to_string_lossy();
- let path = file.path();
- let mut file = File::new(&name,
- path,
- Some(dirty_meta.clone()));
- file.set_tag_status(&tags);
- Some(file)
+ let nonhidden = AtomicUsize::default();
+
+ let direntries: Vec<_> = direntries
+ .into_par_iter()
+ .map(|f| {
+ let f = File::new_from_direntry(f);
+ // Fast check to avoid iterating twice
+ if f.name.as_bytes()[0] != b'.' {
+ nonhidden.fetch_add(1, Ordering::Relaxed);
+ }
+ f
})
.collect();
- let len = files.len();
+ if stale.is_stale()? {
+ HError::stale()?;
+ }
let mut files = Files::default();
- files.directory = File::new_from_path(&path, None)?;
- files.len = len;
+ files.directory = File::new_from_path(&path)?;
+ files.files = direntries;
+ files.len = nonhidden.load(Ordering::Relaxed);
+ files.stale = Some(stale);
Ok(files)
}
- pub fn new_from_path_cancellable(path: &Path,
- stale: Stale)
- -> HResult<Files> {
- let direntries: Result<Vec<_>, _> = std::fs::read_dir(&path)?.collect();
- let dirty = DirtyBit::new();
- let dirty_meta = AsyncDirtyBit::new();
+ pub fn enqueue_jobs(&mut self, n: usize) {
+ let pool = get_pool();
+ let from = self.meta_upto.unwrap_or(0);
+ self.meta_upto = Some(from + n);
+
+ let mut jobs =
+ pool.install(|| {
+ let c = match self.cache.clone() {
+ Some(cache) => cache,
+ None => return vec![]
+ };
+
+ self.iter_files_mut()
+ .skip(from)
+ .take(n)
+ // To turn into IndexedParallelIter
+ .collect::<Vec<&mut File>>()
+ .into_par_iter()
+ .filter_map(|f| f.prepare_meta_job(&c))
+ .collect::<Vec<_>>()
+ });
- let files: Vec<_> = direntries?
- .into_iter()
- .stop_stale(stale.clone())
- .par_bridge()
- .map(|file| {
- let file = File::new_from_direntry(file,
- Some(dirty_meta.clone()));
- file
- })
- .collect();
+ self.jobs.append(&mut jobs);
+ }
- if stale.is_stale()? {
- return Err(crate::fail::HError::StalePreviewError {
- file: path.to_string_lossy().to_string()
- })?;
- }
+ pub fn run_jobs(&mut self, sender: Sender<Events>) {
+ use std::time::Duration;
+ let jobs = std::mem::take(&mut self.jobs);
+ let stale = self.stale.clone()
+ .unwrap_or(Stale::new());
- let mut files = Files {
- directory: File::new_from_path(&path, None)?,
- files: files,
- len: 0,
- pending_events: Arc::new(RwLock::new(vec![])),
- refresh: None,
- meta_upto: None,
- sort: SortBy::Name,
- dirs_first: true,
- reverse: false,
- show_hidden: false,
- filter: None,
- filter_selected: false,
- dirty: dirty,
- };
+ if jobs.len() == 0 { return; }
- files.recalculate_len();
+ std::thread::spawn(move || {
+ let pool = get_pool();
+ let jobs_left = AtomicUsize::new(jobs.len());
+ let jobs_left = &jobs_left;
+ let stale = &stale;
- Ok(files)
+ let ticker = move || {
+ // Gently slow down refreshes
+ let backoff = Duration::from_millis(10);
+ let mut cooldown = Duration::from_millis(10);
+
+ loop {
+ // Send refresh event before sleeping
+ sender.send(crate::widget::Events::WidgetReady)
+ .unwrap();
+ std::thread::sleep(cooldown);
+
+ // Slow down up to 1 second
+ if cooldown < Duration::from_secs(1) {
+ cooldown += backoff;
+ }
+
+ // All jobs done?
+ if jobs_left.load(Ordering::Relaxed) == 0 {
+ // Refresh one last time
+ sender.send(crate::widget::Events::WidgetReady)
+ .unwrap();
+ crate::files::set_ticking(false);
+ return;
+ }
+
+ crate::files::tick();
+ }
+ };
+
+ // To allow calling without consuming, all while Sender can't be shared
+ let mut ticker = Some(ticker);
+
+ // Finally this returns the ticker function as an Option
+ let mut ticker = move || {
+ // Only return ticker if no one's ticking
+ match !crate::files::is_ticking() {
+ true => {
+ crate::files::set_ticking(true);
+ ticker.take()
+ }
+ false => None
+ }
+ };
+
+ pool.scope_fifo(move |s| {
+ // Noop with other pool running ticker
+ ticker().map(|t| s.spawn_fifo(move |_| t()));
+
+ for (path, mslot, dirsize) in jobs.into_iter().stop_stale(stale.clone())
+ {
+ s.spawn_fifo(move |_| {
+ if let Some(mslot) = mslot {
+ if let Ok(meta) = std::fs::symlink_metadata(&path) {
+ *mslot.write().unwrap() = Some(meta);
+ }
+ }
+
+ if let Some(dirsize) = dirsize {
+ std::fs::read_dir(&path)
+ .map(|dirs| {
+ let size = dirs.count();
+ dirsize.0.store(true, Ordering::Relaxed);
+ dirsize.1.store(size, Ordering::Relaxed);
+ }).map_err(|e| {
+ dirsize.0.store(true, Ordering::Relaxed);
+ dirsize.1.store(0, Ordering::Relaxed);
+ HError::from(e)
+ }).log();
+ }
+
+ // Ticker will only stop after this reaches 0
+ jobs_left.fetch_sub(1, Ordering::Relaxed);
+ });
+
+ ticker().map(|t| s.spawn_fifo(move |_| t()));
+ }
+ });
+ });
}
pub fn recalculate_len(&mut self) {
@@ -444,12 +575,14 @@ impl Files {
}
#[allow(trivial_bounds)]
- pub fn into_iter_files(self) -> impl Iterator<Item=File> {
- let filter = self.filter;
+ pub fn into_iter_files(mut self) -> impl Iterator<Item=File> {
+ let filter = std::mem::take(&mut self.filter);
let filter_selected = self.filter_selected;
let show_hidden = self.show_hidden;
- self.files
+ let files = std::mem::take(&mut self.files);
+
+ files
.into_iter()
.filter(move |f|
f.kind == Kind::Placeholder ||
@@ -457,86 +590,122 @@ impl Files {
!f.name.contains(fil