strop_engine/editor/collections/
navigation.rs1use super::{CollectionRow, Editor};
3use strop_core::id::DocumentId;
4
5impl Editor {
6 pub fn collection_open_source_pub(&mut self) {
13 self.collection_open_source();
14 }
15
16 pub(crate) fn collection_open_source(&mut self) {
17 let id = self.current();
18 let cursor_line = self.buf().line_of(self.head());
19 let cursor_col = self.buf().col_of(self.head());
20 let Some(collection) = self.collections.get(&id) else {
21 return;
22 };
23 let mut target: Option<(DocumentId, usize)> = None;
24 for excerpt in &collection.excerpts {
25 if cursor_line == excerpt.view_line {
26 target = Some((excerpt.source, excerpt.start));
28 break;
29 }
30 if cursor_line > excerpt.view_line
31 && cursor_line <= excerpt.view_line + excerpt.view_lines
32 {
33 let Some(source) = self.docs.get(excerpt.source) else {
35 break;
36 };
37 let source_line =
38 source.buf.line_of(excerpt.start) + (cursor_line - excerpt.view_line - 1);
39 let line = source_line.min(source.buf.len_lines().saturating_sub(1));
40 target = Some((
41 excerpt.source,
42 source
43 .buf
44 .clamp_boundary(source.buf.line_start(line).saturating_add(cursor_col)),
45 ));
46 break;
47 }
48 }
49 let Some((document, head)) = target else {
50 self.message = "not on an excerpt".into();
51 return;
52 };
53 if self.docs.get(document).is_none() {
54 self.message = "collection: that source was closed".into();
55 return;
56 }
57 self.push_jump();
58 self.jump_land(document, head);
59 self.lsp_maybe_attach();
60 }
61}
62
63impl Editor {
64 pub fn collection_file_step_pub(&mut self, forward: bool) {
67 self.collection_file_step(forward);
68 }
69
70 pub(crate) fn collection_file_step(&mut self, forward: bool) {
71 let id = self.current();
72 let Some(collection) = self.collections.get(&id) else {
73 self.message = "file cards live in collections".into();
74 return;
75 };
76 let caret = self.buf().line_of(self.head());
77 let mut tops: Vec<usize> = Vec::new();
78 for (row, kind) in collection.rows.iter().enumerate() {
79 if matches!(kind, CollectionRow::CardTop(_)) {
80 tops.push(row);
81 }
82 }
83 let target = if forward {
84 tops.iter().copied().find(|row| *row > caret)
85 } else {
86 tops.iter().copied().rev().find(|row| *row < caret)
87 };
88 let Some(row) = target else {
89 self.message = if forward { "last card" } else { "first card" }.into();
90 return;
91 };
92 self.push_jump();
93 self.set_head(self.buf().line_start(row));
94 self.clamp_cursor();
95 self.scroll_to_cursor(self.view_rows());
96 }
97}
98
99impl Editor {
100 pub fn collection_excerpt_step(&mut self, forward: bool) {
101 let Some(collection) = self.collections.get(&self.current()) else {
102 self.message = "excerpt navigation belongs to a collection".into();
103 return;
104 };
105 let after = collection
106 .excerpts
107 .partition_point(|excerpt| excerpt.view_start <= self.head());
108 let index = if forward {
109 Some(after)
110 } else {
111 after.checked_sub(2)
112 };
113 let target = index
114 .and_then(|index| collection.excerpts.get(index))
115 .map(|excerpt| excerpt.view_start);
116 let Some(target) = target else {
117 self.message = if forward {
118 "last excerpt"
119 } else {
120 "first excerpt"
121 }
122 .into();
123 return;
124 };
125 self.push_jump();
126 self.set_head(target);
127 self.place_jump_target();
128 }
129}