summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorrabite <rabite@posteo.de>2019-03-11 12:08:43 +0100
committerrabite <rabite@posteo.de>2019-03-11 12:08:43 +0100
commite63c65ab7d944fb3a80f6c40bd0d98ec50b054c6 (patch)
tree06ed1d6a0f8cb1a7b13a5b36591680b922abbf32 /src
parent5b9a150a10411dcc47bd6340aa33fa8423aaa2be (diff)
tagging files
Diffstat (limited to 'src')
-rw-r--r--src/fail.rs5
-rw-r--r--src/files.rs74
-rw-r--r--src/listview.rs19
-rw-r--r--src/main.rs4
4 files changed, 98 insertions, 4 deletions
diff --git a/src/fail.rs b/src/fail.rs
index 90def78..16a6028 100644
--- a/src/fail.rs
+++ b/src/fail.rs
@@ -54,6 +54,8 @@ pub enum HError {
StripPrefixError{#[cause] error: std::path::StripPrefixError},
#[fail(display = "INofify failed: {}", error)]
INotifyError{#[cause] error: notify::Error},
+ #[fail(display = "Tags not loaded yet")]
+ TagsNotLoadedYetError
}
impl HError {
@@ -73,6 +75,9 @@ impl HError {
pub fn popup_finnished<T>() -> HResult<T> {
Err(HError::PopupFinnished)
}
+ pub fn tags_not_loaded<T>() -> HResult<T> {
+ Err(HError::TagsNotLoadedYetError)
+ }
}
pub trait ErrorLog where Self: Sized {
diff --git a/src/files.rs b/src/files.rs
index a5e2daa..b151e39 100644
--- a/src/files.rs
+++ b/src/files.rs
@@ -19,6 +19,32 @@ use crate::fail::{HResult, HError};
lazy_static! {
static ref COLORS: LsColors = LsColors::from_env().unwrap();
+ static ref TAGS: Mutex<(bool, Vec<PathBuf>)> = Mutex::new((false, vec![]));
+}
+
+pub fn load_tags() -> HResult<()> {
+ std::thread::spawn(|| -> HResult<()> {
+ let tag_path = crate::paths::tagfile_path()?;
+ let tags = std::fs::read_to_string(tag_path)?;
+ let mut tags = tags.lines().map(|f| PathBuf::from(f)).collect::<Vec<PathBuf>>();
+ let mut tag_lock = TAGS.lock()?;
+ tag_lock.0 = true;
+ tag_lock.1.append(&mut tags);
+ Ok(())
+ });
+ Ok(())
+}
+
+pub fn check_tag(path: &PathBuf) -> HResult<bool> {
+ tags_loaded()?;
+ let tagged = TAGS.lock()?.1.contains(path);
+ Ok(tagged)
+}
+
+pub fn tags_loaded() -> HResult<()> {
+ let loaded = TAGS.lock()?.0;
+ if loaded { Ok(()) }
+ else { HError::tags_not_loaded() }
}
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
@@ -312,7 +338,8 @@ pub struct File {
pub kind: Kind,
pub color: Option<lscolors::Color>,
pub meta: Option<std::fs::Metadata>,
- pub selected: bool
+ pub selected: bool,
+ pub tag: Option<bool>
// flags: Option<String>,
}
@@ -321,13 +348,16 @@ impl File {
name: &str,
path: PathBuf,
) -> File {
+ let tag = check_tag(&path).ok();
+
File {
name: name.to_string(),
kind: if path.is_dir() { Kind::Directory } else { Kind::File },
path: path,
meta: None,
color: None,
- selected: false
+ selected: false,
+ tag: tag,
}
}
@@ -461,6 +491,46 @@ impl File {
self.selected
}
+ pub fn is_tagged(&self) -> HResult<bool> {
+ if let Some(tag) = self.tag {
+ return Ok(tag);
+ }
+ let tag = check_tag(&self.path)?;
+ Ok(tag)
+ }
+
+ pub fn toggle_tag(&mut self) -> HResult<()> {
+ let new_state = match self.tag {
+ Some(tag) => !tag,
+ None => {
+ let tag = check_tag(&self.path);
+ !tag?
+ }
+ };
+ self.tag = Some(new_state);
+
+ match new_state {
+ true => TAGS.lock()?.1.push(self.path.clone()),
+ false => { TAGS.lock()?.1.remove_item(&self.path); },
+ }
+ self.save_tags()?;
+ Ok(())
+ }
+
+ pub fn save_tags(&self) -> HResult<()> {
+ std::thread::spawn(|| -> HResult<()> {
+ let tagfile_path = crate::paths::tagfile_path()?;
+ let tags = TAGS.lock()?.clone();
+ let tags_str = tags.1.iter().map(|p| {
+ let path = p.to_string_lossy().to_string();
+ format!("{}\n", path)
+ }).collect::<String>();
+ std::fs::write(tagfile_path, tags_str)?;
+ Ok(())
+ });
+ Ok(())
+ }
+
pub fn pretty_print_permissions(&self) -> HResult<String> {
let perms: usize = format!("{:o}", self.meta()?.mode()).parse().unwrap();
let perms: usize = perms % 800;
diff --git a/src/listview.rs b/src/listview.rs
index 64ec5ac..b0db7ae 100644
--- a/src/listview.rs
+++ b/src/listview.rs
@@ -46,6 +46,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('t') => self.toggle_tag()?,
Key::Char('h') => self.toggle_hidden(),
Key::Char('r') => self.reverse_sort(),
Key::Char('s') => self.cycle_sort(),
@@ -136,6 +137,11 @@ where
fn render_line(&self, file: &File) -> String {
let name = &file.name;
let (size, unit) = file.calculate_size().unwrap_or((0, "".to_string()));
+ let tag = match file.is_tagged() {
+ Ok(true) => term::color_red() + "*",
+ _ => "".to_string()
+ };
+ let tag_len = if tag != "" { 1 } else { 0 };
let selection_gap = " ".to_string();
let (name, selection_color) = if file.is_selected() {
@@ -149,18 +155,21 @@ where
+ unit.to_string().len() as u16);
let padding = sized_string.len() - sized_string.width_cjk();
let padding = xsize - padding as u16;
+ let padding = padding - tag_len;
format!(
"{}{}{}{}{}{}{}",
termion::cursor::Save,
match &file.color {
- Some(color) => format!("{}{}{:padding$}{}",
+ Some(color) => format!("{}{}{}{:padding$}{}",
+ tag,
term::from_lscolor(color),
selection_color,
&sized_string,
term::normal_color(),
padding = padding as usize),
- _ => format!("{}{}{:padding$}{}",
+ _ => format!("{}{}{}{:padding$}{}",
+ tag,
term::normal_color(),
selection_color,
&sized_string,
@@ -335,6 +344,12 @@ impl ListView<Files>
self.refresh().log();
}
+ fn toggle_tag(&mut self) -> HResult<()> {
+ self.selected_file_mut().toggle_tag()?;
+ self.move_down();
+ Ok(())
+ }
+
fn find_file(&mut self) -> HResult<()> {
let name = self.minibuffer("find")?;
let file = self.content.files.iter().find(|file| {
diff --git a/src/main.rs b/src/main.rs
index f90f8ef..45679eb 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -66,6 +66,10 @@ fn main() -> HResult<()> {
}
fn run() -> HResult<()> {
+ // do this early so it might be ready when needed
+ crate::files::load_tags()?;
+
+
let bufout = std::io::BufWriter::new(std::io::stdout());
// Need to do this here to actually turn terminal into raw mode...
let mut screen = AlternateScreen::from(bufout);