mirror of https://github.com/bobwen-dev/hunter
show fs space usage and too much other stuff
This commit is contained in:
parent
343dd6deda
commit
d5ccfb0d74
|
@ -23,6 +23,8 @@ failure_derive = "0.1.1"
|
|||
notify = "4.0.9"
|
||||
parse-ansi = "0.1.6"
|
||||
signal-notify = "0.1.3"
|
||||
systemstat = "0.1.4"
|
||||
|
||||
|
||||
#[profile.release]
|
||||
#debug = true
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use termion::event::Key;
|
||||
|
||||
use std::io::Write;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
use std::path::PathBuf;
|
||||
use std::ffi::OsString;
|
||||
use std::collections::HashSet;
|
||||
|
@ -23,6 +23,7 @@ use crate::term::ScreenExt;
|
|||
use crate::foldview::LogView;
|
||||
use crate::coordinates::Coordinates;
|
||||
use crate::dirty::Dirtyable;
|
||||
use crate::stats::{FsStat, FsExt};
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum FileBrowserWidgets {
|
||||
|
@ -78,6 +79,7 @@ pub struct FileBrowser {
|
|||
bookmarks: Arc<Mutex<BMPopup>>,
|
||||
log_view: Arc<Mutex<LogView>>,
|
||||
fs_cache: FsCache,
|
||||
fs_stat: Arc<RwLock<FsStat>>
|
||||
}
|
||||
|
||||
impl Tabbable for TabView<FileBrowser> {
|
||||
|
@ -95,6 +97,7 @@ impl Tabbable for TabView<FileBrowser> {
|
|||
tab.proc_view = proc_view;
|
||||
tab.bookmarks = bookmarks;
|
||||
tab.log_view = log_view;
|
||||
tab.fs_stat = cur_tab.fs_stat.clone();
|
||||
|
||||
self.push_widget(tab)?;
|
||||
self.active = self.widgets.len() - 1;
|
||||
|
@ -187,10 +190,14 @@ impl Tabbable for TabView<FileBrowser> {
|
|||
});
|
||||
|
||||
self.active_tab_mut_().fs_cache.watch_only(open_dirs).log();
|
||||
self.active_tab_mut_().fs_stat.write()?.refresh().log();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_config_loaded(&mut self) -> HResult<()> {
|
||||
// hack: wait a bit for widget readyness...
|
||||
std::thread::sleep_ms(100);
|
||||
|
||||
let show_hidden = self.config().show_hidden();
|
||||
for tab in self.widgets.iter_mut() {
|
||||
tab.left_widget_mut().map(|w| {
|
||||
|
@ -198,7 +205,14 @@ impl Tabbable for TabView<FileBrowser> {
|
|||
w.content.dirty_meta.set_dirty();
|
||||
w.refresh().log();
|
||||
}).ok();
|
||||
tab.main_widget_mut().map(|w| w.content.show_hidden = show_hidden).ok();
|
||||
|
||||
tab.main_widget_mut().map(|w| {
|
||||
w.content.show_hidden = show_hidden;
|
||||
w.content.dirty_meta.set_dirty();
|
||||
w.content.sort();
|
||||
w.refresh().log();
|
||||
}).ok();
|
||||
|
||||
tab.preview_widget_mut().map(|w| w.config_loaded()).ok();
|
||||
}
|
||||
Ok(())
|
||||
|
@ -256,6 +270,8 @@ impl FileBrowser {
|
|||
list.select_file(&file);
|
||||
}
|
||||
|
||||
list.content.meta_all();
|
||||
list.content.dirty_meta.set_dirty();
|
||||
list.refresh().log();
|
||||
|
||||
if startup {
|
||||
|
@ -320,6 +336,7 @@ impl FileBrowser {
|
|||
let proc_view = ProcView::new(&core);
|
||||
let bookmarks = BMPopup::new(&core);
|
||||
let log_view = LogView::new(&core, vec![]);
|
||||
let fs_stat = FsStat::new().unwrap();
|
||||
|
||||
|
||||
|
||||
|
@ -331,6 +348,7 @@ impl FileBrowser {
|
|||
bookmarks: Arc::new(Mutex::new(bookmarks)),
|
||||
log_view: Arc::new(Mutex::new(log_view)),
|
||||
fs_cache: fs_cache,
|
||||
fs_stat: Arc::new(RwLock::new(fs_stat))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -636,31 +654,6 @@ impl FileBrowser {
|
|||
HError::no_files()
|
||||
}
|
||||
|
||||
// pub fn take_preview_files(&mut self) -> HResult<Files> {
|
||||
// let widget = self.columns.remove_widget(2);
|
||||
// if let Filxx
|
||||
// }
|
||||
|
||||
// pub fn take_files_from_widget(&self,
|
||||
// widget: FileBrowserWidgets) -> HResult<Files> {
|
||||
// match widget {
|
||||
// FileBrowserWidgets::FileList(file_list) => {
|
||||
// match file_list.take_widget() {
|
||||
// Ok(widget) => {
|
||||
// let files = widget.content;
|
||||
// Ok(files)
|
||||
// }
|
||||
// _ => HError::no_files()
|
||||
// }
|
||||
// }
|
||||
// FileBrowserWidgets::Previewer(previewer) => {
|
||||
// let files = previewer.take_files()?;
|
||||
// Ok(files)
|
||||
// }
|
||||
// _ => HError::no_files()
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn get_files(&self) -> HResult<&Files> {
|
||||
Ok(&self.main_widget()?.content)
|
||||
}
|
||||
|
@ -676,12 +669,12 @@ impl FileBrowser {
|
|||
self.main_widget_mut()?.content.meta_updated = false;
|
||||
|
||||
|
||||
if self.cwd.parent().is_some() {
|
||||
let left_selection = self.left_widget()?.clone_selected_file();
|
||||
let left_files = self.get_left_files()?;
|
||||
self.fs_cache.put_files(left_files, Some(left_selection)).log();
|
||||
self.left_widget_mut()?.content.meta_updated = false;
|
||||
}
|
||||
// if self.cwd.parent().is_some() {
|
||||
// let left_selection = self.left_widget()?.clone_selected_file();
|
||||
// let left_files = self.get_left_files()?;
|
||||
// self.fs_cache.put_files(left_files, Some(left_selection)).log();
|
||||
// self.left_widget_mut()?.content.meta_updated = false;
|
||||
// }
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -929,6 +922,18 @@ impl FileBrowser {
|
|||
let count_xpos = xsize - file_count.len() as u16;
|
||||
let count_ypos = ypos + self.get_coordinates()?.ysize();
|
||||
|
||||
let fs = self.fs_stat.read()?.find_fs(&file.path)?.clone();
|
||||
|
||||
let dev = fs.get_dev();
|
||||
let free_space = fs.get_free();
|
||||
let total_space = fs.get_total();
|
||||
let space = format!("{}: {} / {}",
|
||||
dev,
|
||||
free_space,
|
||||
total_space);
|
||||
|
||||
let space_xpos = count_xpos - space.len() as u16 - 5; // - 3;
|
||||
|
||||
let status = format!("{} {}:{} {}{} {}{}",
|
||||
permissions,
|
||||
user,
|
||||
|
@ -940,10 +945,13 @@ impl FileBrowser {
|
|||
);
|
||||
let status = crate::term::sized_string_u(&status, (xsize-1) as usize);
|
||||
|
||||
let status = format!("{}{}{}{}",
|
||||
let status = format!("{}{}{}{}{}{} | {}",
|
||||
status,
|
||||
crate::term::header_color(),
|
||||
crate::term::goto_xy(count_xpos, count_ypos),
|
||||
crate::term::goto_xy(space_xpos, count_ypos),
|
||||
crate::term::color_orange(),
|
||||
space,
|
||||
crate::term::header_color(),
|
||||
file_count);
|
||||
|
||||
Ok(status)
|
||||
|
@ -972,9 +980,13 @@ impl Widget for FileBrowser {
|
|||
let file = self.selected_file()?;
|
||||
let name = &file.name;
|
||||
|
||||
let color = if file.is_dir() || file.color.is_none() {
|
||||
crate::term::highlight_color() } else {
|
||||
crate::term::from_lscolor(file.color.as_ref().unwrap()) };
|
||||
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())
|
||||
};
|
||||
|
||||
let path = self.cwd.short_string();
|
||||
|
||||
|
|
19
src/files.rs
19
src/files.rs
|
@ -329,8 +329,21 @@ impl Files {
|
|||
pub fn replace_file(&mut self,
|
||||
old: Option<&File>,
|
||||
new: Option<File>) -> HResult<()> {
|
||||
let (tag, selected) = if let Some(old) = old {
|
||||
if let Some(old) = self.find_file_with_path(&old.path) {
|
||||
(old.tag, old.selected)
|
||||
} else {
|
||||
(None, false)
|
||||
}
|
||||
} else {
|
||||
(None, false)
|
||||
};
|
||||
old.map(|old| self.files.remove_item(old));
|
||||
new.map(|new| self.files.push(new));
|
||||
new.map(|mut new| {
|
||||
new.tag = tag;
|
||||
new.selected = selected;
|
||||
self.files.push(new);
|
||||
});
|
||||
self.sort();
|
||||
Ok(())
|
||||
}
|
||||
|
@ -913,7 +926,7 @@ impl File {
|
|||
if file_user.name() == cur_user {
|
||||
crate::term::color_green()
|
||||
} else {
|
||||
crate::term::color_yellow() };
|
||||
crate::term::color_red() };
|
||||
Some(format!("{}{}", color, file_user.name().to_string_lossy()))
|
||||
}
|
||||
|
||||
|
@ -926,7 +939,7 @@ impl File {
|
|||
if file_group.name() == cur_group {
|
||||
crate::term::color_green()
|
||||
} else {
|
||||
crate::term::color_yellow() };
|
||||
crate::term::color_red() };
|
||||
Some(format!("{}{}", color, file_group.name().to_string_lossy()))
|
||||
}
|
||||
|
||||
|
|
|
@ -115,8 +115,10 @@ impl FsCache {
|
|||
self.add_watch(&dir).log();
|
||||
let dir = dir.clone();
|
||||
let selection = self.get_selection(&dir).ok();
|
||||
let cache = self.clone();
|
||||
let files = Async::new(Box::new(move |_| {
|
||||
let files = Files::new_from_path_cancellable(&dir.path, stale)?;
|
||||
let mut files = Files::new_from_path_cancellable(&dir.path, stale)?;
|
||||
FsCache::apply_settingss(&cache, &mut files).ok();
|
||||
Ok(files)
|
||||
}));
|
||||
Ok((selection, files))
|
||||
|
@ -126,7 +128,9 @@ impl FsCache {
|
|||
pub fn get_files_sync(&self, dir: &File) -> HResult<Files> {
|
||||
self.add_watch(&dir).log();
|
||||
let files = self.get_files(&dir, Stale::new())?.1;
|
||||
files.wait()
|
||||
let mut files = files.wait()?;
|
||||
FsCache::apply_settingss(&self, &mut files).ok();
|
||||
Ok(files)
|
||||
}
|
||||
|
||||
pub fn get_selection(&self, dir: &File) -> HResult<File> {
|
||||
|
@ -232,6 +236,36 @@ impl FsCache {
|
|||
Ok((selection, files))
|
||||
}
|
||||
|
||||
|
||||
pub fn apply_settingss(cache: &FsCache,
|
||||
files: &mut Files)
|
||||
-> HResult<()> {
|
||||
let dir = &files.directory;
|
||||
let tab_settings = cache.tab_settings.read()?.get(&dir).cloned();
|
||||
if tab_settings.is_none() { return Ok(()) }
|
||||
let tab_settings = tab_settings?;
|
||||
|
||||
files.sort = tab_settings.dir_settings.sort;
|
||||
files.dirs_first = tab_settings.dir_settings.dirs_first;
|
||||
files.reverse = tab_settings.dir_settings.reverse;
|
||||
files.show_hidden = tab_settings.dir_settings.show_hidden;
|
||||
files.filter = tab_settings.dir_settings.filter.clone();
|
||||
|
||||
if tab_settings.multi_selections.len() > 0 {
|
||||
for file in &mut files.files {
|
||||
for selected_files in &tab_settings.multi_selections {
|
||||
if file.path == selected_files.path {
|
||||
file.selected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
files.sort();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
fn extract_tab_settings(files: &Files, selection: Option<File>) -> TabSettings {
|
||||
TabSettings {
|
||||
selection: selection,
|
||||
|
|
|
@ -65,6 +65,8 @@ impl Listable for ListView<Files> {
|
|||
self.move_down();
|
||||
self.refresh()?;
|
||||
},
|
||||
Key::Char('<') => self.move_top(),
|
||||
Key::Char('>') => self.move_bottom(),
|
||||
Key::Char('S') => { self.search_file().log(); }
|
||||
Key::Alt('s') => { self.search_next().log(); }
|
||||
Key::Alt('S') => { self.search_prev().log(); }
|
||||
|
@ -72,6 +74,7 @@ impl Listable for ListView<Files> {
|
|||
Key::Left => self.goto_grand_parent()?,
|
||||
Key::Right => self.goto_selected()?,
|
||||
Key::Char(' ') => self.multi_select_file(),
|
||||
Key::Char('v') => self.invert_selection(),
|
||||
Key::Char('t') => self.toggle_tag()?,
|
||||
Key::Char('h') => self.toggle_hidden(),
|
||||
Key::Char('r') => self.reverse_sort(),
|
||||
|
@ -146,6 +149,15 @@ where
|
|||
self.seeking = false;
|
||||
}
|
||||
|
||||
pub fn move_top(&mut self) {
|
||||
self.set_selection(0);
|
||||
}
|
||||
|
||||
pub fn move_bottom(&mut self) {
|
||||
let lines = self.lines;
|
||||
self.set_selection(lines - 1);
|
||||
}
|
||||
|
||||
pub fn get_selection(&self) -> usize {
|
||||
self.selection
|
||||
}
|
||||
|
@ -154,8 +166,7 @@ where
|
|||
let ysize = self.get_coordinates().unwrap().ysize() as usize;
|
||||
let mut offset = 0;
|
||||
|
||||
while position + 2
|
||||
>= ysize + offset {
|
||||
while position >= ysize + offset {
|
||||
offset += 1
|
||||
}
|
||||
|
||||
|
@ -175,7 +186,7 @@ impl ListView<Files>
|
|||
|
||||
pub fn selected_file_mut(&mut self) -> &mut File {
|
||||
let selection = self.selection;
|
||||
let mut file = self.content.get_file_mut(selection);
|
||||
let file = self.content.get_file_mut(selection);
|
||||
file.unwrap()
|
||||
}
|
||||
|
||||
|
@ -325,6 +336,14 @@ impl ListView<Files>
|
|||
self.move_down();
|
||||
}
|
||||
|
||||
pub fn invert_selection(&mut self) {
|
||||
for file in self.content.get_files_mut() {
|
||||
file.toggle_selection();
|
||||
}
|
||||
self.content.set_dirty();
|
||||
self.refresh().log();
|
||||
}
|
||||
|
||||
fn toggle_tag(&mut self) -> HResult<()> {
|
||||
self.selected_file_mut().toggle_tag()?;
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ extern crate notify;
|
|||
extern crate parse_ansi;
|
||||
extern crate signal_notify;
|
||||
extern crate tree_magic;
|
||||
extern crate systemstat;
|
||||
|
||||
use failure::Fail;
|
||||
|
||||
|
@ -50,7 +51,7 @@ mod foldview;
|
|||
mod dirty;
|
||||
mod fscache;
|
||||
mod config;
|
||||
|
||||
mod stats;
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -644,7 +644,7 @@ impl Previewer {
|
|||
|
||||
if is_stale(&stale)? { return Previewer::preview_failed(&file) }
|
||||
|
||||
let output = dbg!(process.wait_with_output())?;
|
||||
let output = process.wait_with_output()?;
|
||||
|
||||
if is_stale(&stale)? { return Previewer::preview_failed(&file) }
|
||||
{
|
||||
|
@ -652,7 +652,7 @@ impl Previewer {
|
|||
*pid_ = None;
|
||||
}
|
||||
|
||||
let status = output.status.code()?;
|
||||
//let status = output.status.code()?;
|
||||
|
||||
if !is_stale(&stale)? {
|
||||
let output = std::str::from_utf8(&output.stdout)
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
use systemstat::{System, Platform};
|
||||
use systemstat::data::Filesystem;
|
||||
|
||||
use std::path::{Path, PathBuf, Component};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::fail::{HResult, ErrorLog};
|
||||
|
||||
#[derive(Debug,Clone)]
|
||||
pub struct FsStat {
|
||||
pub stats: HashMap<PathBuf, Filesystem>
|
||||
}
|
||||
|
||||
impl FsStat {
|
||||
pub fn new() -> HResult<FsStat> {
|
||||
let mut stats = FsStat { stats: HashMap::new() };
|
||||
stats.refresh().log();
|
||||
|
||||
Ok(stats)
|
||||
}
|
||||
|
||||
pub fn refresh(&mut self) -> HResult<()> {
|
||||
let sys = System::new();
|
||||
let mounts = sys.mounts()?;
|
||||
|
||||
let stats = mounts.into_iter()
|
||||
.fold(HashMap::new(), |mut stats, mount: Filesystem| {
|
||||
let path = PathBuf::from(&mount.fs_mounted_on);
|
||||
stats.insert(path, mount);
|
||||
stats
|
||||
});
|
||||
|
||||
self.stats = stats;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn find_fs(&self, path: &Path) -> HResult<&Filesystem> {
|
||||
let candidates = self
|
||||
.stats
|
||||
.keys()
|
||||
.filter(|mount_point| path.starts_with(&mount_point))
|
||||
.collect::<Vec<&PathBuf>>();
|
||||
|
||||
let deepest_match = candidates.iter()
|
||||
.fold(PathBuf::new(), |mut deepest, path| {
|
||||
let curren_path_len = deepest.components().count();
|
||||
let candidate_path_len = path.components().count();
|
||||
|
||||
if candidate_path_len > curren_path_len {
|
||||
deepest = path.to_path_buf();
|
||||
}
|
||||
deepest
|
||||
});
|
||||
let fs = self.stats.get(&deepest_match)?;
|
||||
Ok(fs)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FsExt {
|
||||
fn get_dev(&self) -> String;
|
||||
fn get_total(&self) -> String;
|
||||
fn get_free(&self) -> String;
|
||||
}
|
||||
|
||||
impl FsExt for Filesystem {
|
||||
fn get_dev(&self) -> String {
|
||||
let path = PathBuf::from(&self.fs_mounted_from);
|
||||
let dev = path.components().last().unwrap();
|
||||
let dev = match dev {
|
||||
Component::Normal(dev) => dev.to_string_lossy().to_string(),
|
||||
_ => "wtf".to_string()
|
||||
};
|
||||
dev
|
||||
}
|
||||
|
||||
fn get_total(&self) -> String {
|
||||
self.total.to_string(false)
|
||||
}
|
||||
|
||||
fn get_free(&self) -> String {
|
||||
self.free.to_string(false)
|
||||
}
|
||||
}
|
44
src/term.rs
44
src/term.rs
|
@ -249,16 +249,44 @@ pub fn color_green() -> String {
|
|||
format!("{}", termion::color::Fg(termion::color::Green))
|
||||
}
|
||||
|
||||
pub fn color_light_green() -> String {
|
||||
format!("{}", termion::color::Fg(termion::color::LightGreen))
|
||||
}
|
||||
|
||||
pub fn color_cyan() -> String {
|
||||
format!("{}", termion::color::Fg(termion::color::Cyan))
|
||||
}
|
||||
|
||||
pub fn color_light_yellow() -> String {
|
||||
format!("{}", termion::color::Fg(termion::color::LightYellow))
|
||||
}
|
||||
|
||||
pub fn color_orange() -> String {
|
||||
let color = termion::color::Fg(termion::color::AnsiValue::rgb(5 as u8 ,
|
||||
4 as u8,
|
||||
0 as u8));
|
||||
format!("{}", color)
|
||||
}
|
||||
|
||||
|
||||
pub fn from_lscolor(color: &lscolors::Color) -> String {
|
||||
match color {
|
||||
lscolors::Color::Black => format!("{}", termion::color::Fg(termion::color::Black)),
|
||||
lscolors::Color::Red => format!("{}", termion::color::Fg(termion::color::Red)),
|
||||
lscolors::Color::Green => format!("{}", termion::color::Fg(termion::color::Green)),
|
||||
lscolors::Color::Yellow => format!("{}", termion::color::Fg(termion::color::Yellow)),
|
||||
lscolors::Color::Blue => format!("{}", termion::color::Fg(termion::color::Blue)),
|
||||
lscolors::Color::Magenta => format!("{}", termion::color::Fg(termion::color::Magenta)),
|
||||
lscolors::Color::Cyan => format!("{}", termion::color::Fg(termion::color::Cyan)),
|
||||
lscolors::Color::White => format!("{}", termion::color::Fg(termion::color::White)),
|
||||
lscolors::Color::Black
|
||||
=> format!("{}", termion::color::Fg(termion::color::Black)),
|
||||
lscolors::Color::Red
|
||||
=> format!("{}", termion::color::Fg(termion::color::Red)),
|
||||
lscolors::Color::Green
|
||||
=> format!("{}", termion::color::Fg(termion::color::Green)),
|
||||
lscolors::Color::Yellow
|
||||
=> format!("{}", termion::color::Fg(termion::color::Yellow)),
|
||||
lscolors::Color::Blue
|
||||
=> format!("{}", termion::color::Fg(termion::color::Blue)),
|
||||
lscolors::Color::Magenta
|
||||
=> format!("{}", termion::color::Fg(termion::color::Magenta)),
|
||||
lscolors::Color::Cyan
|
||||
=> format!("{}", termion::color::Fg(termion::color::Cyan)),
|
||||
lscolors::Color::White
|
||||
=> format!("{}", termion::color::Fg(termion::color::White)),
|
||||
_ => format!("{}", normal_color()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -392,7 +392,7 @@ pub trait Widget {
|
|||
}
|
||||
Events::ConfigLoaded => {
|
||||
self.get_core_mut()?.config.write()?.take_async().log();
|
||||
self.config_loaded();
|
||||
self.config_loaded().log();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue