1use std::{
2 cmp::min,
3 path::{Path, PathBuf},
4};
5
6use crate::common::tilde;
7use crate::io::{DrawMenu, Extension};
8use crate::modes::extract_extension;
9use crate::{impl_content, impl_selectable};
10
11#[derive(Default)]
14pub struct Flagged {
15 pub content: Vec<PathBuf>,
22 pub index: usize,
24}
25
26impl Flagged {
27 pub fn update(&mut self, content: Vec<PathBuf>) {
28 self.content = content;
29 self.content.sort();
30 self.index = 0;
31 }
32
33 pub fn extend(&mut self, mut content: Vec<PathBuf>) {
34 self.content.append(&mut content);
35 self.content.sort();
36 self.index = 0;
37 }
38
39 pub fn clear(&mut self) {
40 self.content = vec![];
41 self.index = 0;
42 }
43
44 pub fn remove_selected(&mut self) {
45 self.content.remove(self.index);
46 self.index = self.index.saturating_sub(1);
47 }
48
49 pub fn push(&mut self, path: PathBuf) {
52 let Err(pos) = self.content.binary_search(&path) else {
53 return;
54 };
55 self.content.insert(pos, path);
56 }
57
58 pub fn toggle(&mut self, path: &Path) {
62 let path = path.to_path_buf();
63 match self.content.binary_search(&path) {
64 Ok(pos) => self.remove_index(pos),
65 Err(pos) => self.content.insert(pos, path),
66 }
67 }
68
69 fn remove_index(&mut self, index: usize) {
70 self.content.remove(index);
71 if self.index >= self.len() {
72 self.index = self.index.saturating_sub(1);
73 }
74 }
75
76 #[inline]
80 #[must_use]
81 pub fn contains(&self, path: &Path) -> bool {
82 self.content.binary_search(&path.to_path_buf()).is_ok()
83 }
84
85 #[inline]
87 #[must_use]
88 pub fn in_dir(&self, dir: &Path) -> Vec<PathBuf> {
89 self.content
90 .iter()
91 .filter(|p| p.starts_with(dir))
92 .map(|p| p.to_owned())
93 .collect()
94 }
95
96 pub fn content_to_string(&self) -> String {
98 self.content()
99 .iter()
100 .map(|path| path.to_string_lossy().into_owned())
101 .collect::<Vec<String>>()
102 .join("\n")
103 }
104
105 pub fn replace_by_string(&mut self, files: String) {
106 self.clear();
107 files.lines().for_each(|f| {
108 let p = PathBuf::from(tilde(f).as_ref());
109 if p.exists() {
110 self.push(p);
111 }
112 });
113 }
114
115 pub fn as_strings(&self) -> Vec<String> {
117 self.content
118 .iter()
119 .map(|p| p.to_string_lossy().to_string())
120 .collect()
121 }
122
123 fn should_this_file_be_opened_in_neovim(&self, path: &Path) -> bool {
124 matches!(Extension::matcher(extract_extension(path)), Extension::Text)
125 }
126
127 pub fn should_all_be_opened_in_neovim(&self) -> bool {
128 self.content()
129 .iter()
130 .all(|path| self.should_this_file_be_opened_in_neovim(path))
131 }
132
133 pub fn remove_non_existant(&mut self) {
135 let non_existant_indices: Vec<usize> = self
136 .content
137 .iter()
138 .enumerate()
139 .filter(|(_index, path)| !path.exists())
140 .map(|(index, _path)| index)
141 .rev()
142 .collect();
143 for index in non_existant_indices.iter() {
144 self.content.remove(*index);
145 }
146 self.index = min(self.index, self.len().saturating_sub(1))
147 }
148}
149
150impl_selectable!(Flagged);
151impl_content!(Flagged, PathBuf);
152
153impl DrawMenu<PathBuf> for Flagged {}