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 if !path.exists() {
53 return;
54 }
55 let Err(pos) = self.content.binary_search(&path) else {
56 return;
57 };
58 self.content.insert(pos, path);
59 }
60
61 pub fn toggle(&mut self, path: &Path) {
65 let path = path.to_path_buf();
66 match self.content.binary_search(&path) {
67 Ok(pos) => self.remove_index(pos),
68 Err(pos) => self.content.insert(pos, path),
69 }
70 }
71
72 fn remove_index(&mut self, index: usize) {
73 self.content.remove(index);
74 if self.index >= self.len() {
75 self.index = self.index.saturating_sub(1);
76 }
77 }
78
79 #[inline]
83 #[must_use]
84 pub fn contains(&self, path: &Path) -> bool {
85 self.content.binary_search(&path.to_path_buf()).is_ok()
86 }
87
88 #[inline]
91 #[must_use]
92 pub fn in_dir(&self, dir: &Path) -> Vec<PathBuf> {
93 self.content
94 .iter()
95 .filter(|p| *p != dir)
96 .filter(|p| p.starts_with(dir))
97 .map(|p| p.to_owned())
98 .collect()
99 }
100
101 pub fn content_to_string(&self) -> String {
103 self.content()
104 .iter()
105 .map(|path| path.to_string_lossy().into_owned())
106 .collect::<Vec<String>>()
107 .join("\n")
108 }
109
110 pub fn replace_by_string(&mut self, files: String) {
111 self.clear();
112 files.lines().for_each(|f| {
113 let p = PathBuf::from(tilde(f).as_ref());
114 if p.exists() {
115 self.push(p);
116 }
117 });
118 }
119
120 pub fn as_strings(&self) -> Vec<String> {
122 self.content
123 .iter()
124 .map(|p| p.to_string_lossy().to_string())
125 .collect()
126 }
127
128 fn should_this_file_be_opened_in_neovim(&self, path: &Path) -> bool {
129 matches!(Extension::matcher(extract_extension(path)), Extension::Text)
130 }
131
132 pub fn should_all_be_opened_in_neovim(&self) -> bool {
133 self.content()
134 .iter()
135 .all(|path| self.should_this_file_be_opened_in_neovim(path))
136 }
137
138 pub fn remove_non_existant(&mut self) {
140 let non_existant_indices: Vec<usize> = self
141 .content
142 .iter()
143 .enumerate()
144 .filter(|(_index, path)| !path.exists())
145 .map(|(index, _path)| index)
146 .rev()
147 .collect();
148 for index in non_existant_indices.iter() {
149 self.content.remove(*index);
150 }
151 self.index = min(self.index, self.len().saturating_sub(1))
152 }
153}
154
155impl_content!(Flagged, PathBuf);
156
157impl DrawMenu<PathBuf> for Flagged {}