1use crate::action::{Action, ActionError};
11use crate::changeset::{ChangeSet, Edit};
12use crate::history::EditKind;
13use crate::resolve::Resolved;
14use crate::selection::SelectionSet;
15use crate::text::Document;
16
17#[derive(Clone, Debug, Default, PartialEq, Eq)]
19pub struct Bookmarks {
20 lines: Vec<usize>,
21}
22
23#[derive(Clone, Copy, Debug)]
27pub(crate) struct EditLines {
28 pub start: usize,
29 pub old_end: usize,
30 pub new_lines: usize,
32 pub at_line_start: bool,
34 pub insertion: bool,
36}
37
38impl Bookmarks {
39 pub fn lines(&self) -> &[usize] {
41 &self.lines
42 }
43
44 pub fn is_empty(&self) -> bool {
45 self.lines.is_empty()
46 }
47
48 pub fn len(&self) -> usize {
49 self.lines.len()
50 }
51
52 pub fn contains(&self, line: usize) -> bool {
53 self.lines.binary_search(&line).is_ok()
54 }
55
56 pub fn set(&mut self, lines: impl IntoIterator<Item = usize>, line_count: usize) {
59 let mut lines: Vec<usize> = lines.into_iter().filter(|&l| l < line_count).collect();
60 lines.sort_unstable();
61 lines.dedup();
62 self.lines = lines;
63 }
64
65 pub fn insert(&mut self, line: usize) -> bool {
67 match self.lines.binary_search(&line) {
68 Ok(_) => false,
69 Err(i) => {
70 self.lines.insert(i, line);
71 true
72 }
73 }
74 }
75
76 pub fn remove(&mut self, line: usize) -> bool {
78 match self.lines.binary_search(&line) {
79 Ok(i) => {
80 self.lines.remove(i);
81 true
82 }
83 Err(_) => false,
84 }
85 }
86
87 pub fn toggle(&mut self, line: usize) -> bool {
90 !self.remove(line) && self.insert(line)
91 }
92
93 pub fn clear(&mut self) {
94 self.lines.clear();
95 }
96
97 pub fn next_after(&self, line: usize) -> Option<usize> {
100 let i = self.lines.partition_point(|&l| l <= line);
101 self.lines.get(i).or(self.lines.first()).copied()
102 }
103
104 pub fn prev_before(&self, line: usize) -> Option<usize> {
106 let i = self.lines.partition_point(|&l| l < line);
107 i.checked_sub(1)
108 .and_then(|i| self.lines.get(i))
109 .or(self.lines.last())
110 .copied()
111 }
112
113 pub fn invert(&mut self, line_count: usize) {
116 let mut marked = self.lines.iter().copied().peekable();
117 let mut inverted = Vec::with_capacity(line_count.saturating_sub(self.lines.len()));
118 for line in 0..line_count {
119 if marked.peek() == Some(&line) {
120 marked.next();
121 } else {
122 inverted.push(line);
123 }
124 }
125 self.lines = inverted;
126 }
127
128 pub(crate) fn map(&mut self, edits: &[EditLines], line_count: usize) {
131 if self.lines.is_empty() || edits.is_empty() {
132 return;
133 }
134 let mut out = Vec::with_capacity(self.lines.len());
135 let mut delta: isize = 0;
137 let mut i = 0;
138 for &line in &self.lines {
139 while let Some(e) = edits.get(i).filter(|e| e.old_end < line) {
140 delta += e.new_lines as isize - (e.old_end - e.start) as isize;
141 i += 1;
142 }
143 let new = match edits.get(i) {
144 Some(e) if e.start < line => {
147 e.start as isize + delta + (line - e.start).min(e.new_lines) as isize
148 }
149 Some(e) if e.start == line && e.at_line_start && e.insertion => {
151 (line + e.new_lines) as isize + delta
152 }
153 _ => line as isize + delta,
154 };
155 out.push(new.clamp(0, line_count.saturating_sub(1) as isize) as usize);
156 }
157 out.sort_unstable();
158 out.dedup();
159 self.lines = out;
160 }
161}
162
163pub fn marked_text(doc: &Document, marks: &Bookmarks) -> String {
166 let last = doc.line_count() - 1;
167 let mut text = String::new();
168 for &line in marks.lines() {
169 text.push_str(&doc.line_text(line));
170 if line < last {
171 text.push('\n');
172 }
173 }
174 text
175}
176
177pub(crate) fn caret_lines(doc: &Document, sels: &SelectionSet) -> Vec<usize> {
179 let mut lines: Vec<usize> = sels.ranges().iter().map(|s| doc.line_of(s.head)).collect();
180 lines.dedup();
181 lines
182}
183
184pub(crate) fn resolve(
186 action: &Action,
187 doc: &Document,
188 sels: &SelectionSet,
189 marks: &Bookmarks,
190) -> Result<Resolved, ActionError> {
191 if marks.is_empty() {
192 return Err(ActionError::NoBookmarks);
193 }
194 let caret = doc.line_of(sels.primary().head);
195 let go = |line: Option<usize>| {
196 let line = line.ok_or(ActionError::NoBookmarks)?;
197 Ok(Resolved::Selections(SelectionSet::caret(
198 doc.line_start(line),
199 )))
200 };
201 match action {
202 Action::NextBookmark => go(marks.next_after(caret)),
203 Action::PrevBookmark => go(marks.prev_before(caret)),
204 Action::RemoveBookmarkedLines => {
205 Ok(crate::transform::remove_lines(doc, sels, marks.lines()))
206 }
207 Action::RemoveUnbookmarkedLines => {
208 let last = doc.line_count() - 1;
211 let count = if last > 0 && doc.line_start(last) == doc.len() {
212 last
213 } else {
214 last + 1
215 };
216 let mut unmarked = marks.clone();
217 unmarked.invert(count);
218 Ok(crate::transform::remove_lines(doc, sels, unmarked.lines()))
219 }
220 Action::ReplaceBookmarkedLines(text) => {
221 let edits: Vec<Edit> = marks
222 .lines()
223 .iter()
224 .map(|&l| Edit::new(doc.line_start(l), doc.line_end(l), text.as_str()))
225 .collect();
226 let changes = ChangeSet::new(doc.len(), edits);
227 let after = SelectionSet::caret(
228 changes.map_position(sels.primary().head, crate::changeset::Bias::Before),
229 );
230 Ok(Resolved::Edit {
231 changes,
232 after,
233 kind: EditKind::Other,
234 })
235 }
236 _ => Ok(Resolved::Selections(sels.clone())),
237 }
238}
239
240pub(crate) fn after_replace(before: &[usize], text: &str) -> Vec<usize> {
243 let breaks = text.bytes().filter(|&b| b == b'\n').count();
244 before
245 .iter()
246 .enumerate()
247 .map(|(i, &l)| l + i * breaks)
248 .collect()
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254
255 fn marks(lines: &[usize]) -> Bookmarks {
256 let mut b = Bookmarks::default();
257 b.set(lines.iter().copied(), usize::MAX);
258 b
259 }
260
261 #[test]
262 fn toggle_insert_remove_and_set() {
263 let mut b = Bookmarks::default();
264 assert!(b.toggle(3));
265 assert!(b.toggle(1));
266 assert_eq!(b.lines(), &[1, 3]);
267 assert!(!b.toggle(3));
268 assert_eq!(b.lines(), &[1]);
269 assert!(!b.insert(1));
270 assert!(b.remove(1));
271 assert!(b.is_empty());
272 b.set([5, 2, 2, 9], 6);
273 assert_eq!(b.lines(), &[2, 5]);
274 }
275
276 #[test]
277 fn next_and_previous_wrap_around() {
278 let b = marks(&[2, 5, 9]);
279 assert_eq!(b.next_after(0), Some(2));
280 assert_eq!(b.next_after(2), Some(5));
281 assert_eq!(b.next_after(9), Some(2));
282 assert_eq!(b.prev_before(5), Some(2));
283 assert_eq!(b.prev_before(2), Some(9));
284 assert_eq!(b.prev_before(100), Some(9));
285 assert_eq!(Bookmarks::default().next_after(0), None);
286 }
287
288 #[test]
289 fn invert_marks_the_other_lines() {
290 let mut b = marks(&[0, 2]);
291 b.invert(5);
292 assert_eq!(b.lines(), &[1, 3, 4]);
293 b.invert(5);
294 assert_eq!(b.lines(), &[0, 2]);
295 }
296
297 #[test]
298 fn marked_text_keeps_line_breaks_except_at_the_end() {
299 let doc = Document::new("a\nb\nc");
300 assert_eq!(marked_text(&doc, &marks(&[0, 2])), "a\nc");
301 assert_eq!(marked_text(&doc, &marks(&[1])), "b\n");
302 }
303
304 #[test]
305 fn after_replace_counts_the_inserted_lines() {
306 assert_eq!(after_replace(&[1, 4], "x\ny"), vec![1, 5]);
307 assert_eq!(after_replace(&[1, 4], "x"), vec![1, 4]);
308 }
309}