on master help sign in
Introduce doc.selection()/set_selection()
55fa862Blaž Hrastnik
helix-view/src/commands.rs+67−75
@@ -19,40 +19,38 @@ use crate::{
19pub type Command = fn(view: &mut View, count: usize);
19pub type Command = fn(view: &mut View, count: usize);
20
20
21pub fn move_char_left(view: &mut View, count: usize) {
21pub fn move_char_left(view: &mut View, count: usize) {
22− // TODO: use a transaction
23 let selection =
22 let selection =
24 view.doc
23 view.doc
25 .state
24 .state
26 .move_selection(Direction::Backward, Granularity::Character, count);
25 .move_selection(Direction::Backward, Granularity::Character, count);
27− view.doc.state.selection = selection;
26+ view.doc.set_selection(selection);
28}
27}
29
28
30pub fn move_char_right(view: &mut View, count: usize) {
29pub fn move_char_right(view: &mut View, count: usize) {
31− // TODO: use a transaction
32− view.doc.state.selection =
30+ let selection =
33 view.doc
31 view.doc
34 .state
32 .state
35 .move_selection(Direction::Forward, Granularity::Character, count);
33 .move_selection(Direction::Forward, Granularity::Character, count);
34+ view.doc.set_selection(selection);
36}
35}
37
36
38pub fn move_line_up(view: &mut View, count: usize) {
37pub fn move_line_up(view: &mut View, count: usize) {
39− // TODO: use a transaction
40− view.doc.state.selection =
38+ let selection = view
41− view.doc
39+ .doc
42− .state
40+ .state
43− .move_selection(Direction::Backward, Granularity::Line, count);
41+ .move_selection(Direction::Backward, Granularity::Line, count);
42+ view.doc.set_selection(selection);
44}
43}
45
44
46pub fn move_line_down(view: &mut View, count: usize) {
45pub fn move_line_down(view: &mut View, count: usize) {
47− // TODO: use a transaction
48− view.doc.state.selection =
46+ let selection = view
49− view.doc
47+ .doc
50− .state
48+ .state
51− .move_selection(Direction::Forward, Granularity::Line, count);
49+ .move_selection(Direction::Forward, Granularity::Line, count);
50+ view.doc.set_selection(selection);
52}
51}
53
52
54pub fn move_line_end(view: &mut View, _count: usize) {
53pub fn move_line_end(view: &mut View, _count: usize) {
55− // TODO: use a transaction
56 let lines = selection_lines(&view.doc.state);
54 let lines = selection_lines(&view.doc.state);
57
55
58 let positions = lines
56 let positions = lines
@@ -68,9 +66,7 @@ pub fn move_line_end(view: &mut View, _count: usize) {
68
66
69 let selection = Selection::new(positions.collect(), 0);
67 let selection = Selection::new(positions.collect(), 0);
70
68
71− let transaction = Transaction::new(&mut view.doc.state).with_selection(selection);
69+ view.doc.set_selection(selection);
72−
73− view.doc.apply(&transaction);
74}
70}
75
71
76pub fn move_line_start(view: &mut View, _count: usize) {
72pub fn move_line_start(view: &mut View, _count: usize) {
@@ -86,64 +82,58 @@ pub fn move_line_start(view: &mut View, _count: usize) {
86
82
87 let selection = Selection::new(positions.collect(), 0);
83 let selection = Selection::new(positions.collect(), 0);
88
84
89− let transaction = Transaction::new(&mut view.doc.state).with_selection(selection);
85+ view.doc.set_selection(selection);
90−
91− view.doc.apply(&transaction);
92}
86}
93
87
94pub fn move_next_word_start(view: &mut View, count: usize) {
88pub fn move_next_word_start(view: &mut View, count: usize) {
95 let pos = view.doc.state.move_pos(
89 let pos = view.doc.state.move_pos(
96− view.doc.state.selection.cursor(),
90+ view.doc.selection().cursor(),
97 Direction::Forward,
91 Direction::Forward,
98 Granularity::Word,
92 Granularity::Word,
99 count,
93 count,
100 );
94 );
101
95
102− // TODO: use a transaction
103− view.doc.state.selection = Selection::single(pos, pos);
96+ view.doc.set_selection(Selection::single(pos, pos));
104}
97}
105
98
106pub fn move_prev_word_start(view: &mut View, count: usize) {
99pub fn move_prev_word_start(view: &mut View, count: usize) {
107 let pos = view.doc.state.move_pos(
100 let pos = view.doc.state.move_pos(
108− view.doc.state.selection.cursor(),
101+ view.doc.selection().cursor(),
109 Direction::Backward,
102 Direction::Backward,
110 Granularity::Word,
103 Granularity::Word,
111 count,
104 count,
112 );
105 );
113
106
114− // TODO: use a transaction
115− view.doc.state.selection = Selection::single(pos, pos);
107+ view.doc.set_selection(Selection::single(pos, pos));
116}
108}
117
109
118pub fn move_next_word_end(view: &mut View, count: usize) {
110pub fn move_next_word_end(view: &mut View, count: usize) {
119 let pos = State::move_next_word_end(
111 let pos = State::move_next_word_end(
120 &view.doc.text().slice(..),
112 &view.doc.text().slice(..),
121− view.doc.state.selection.cursor(),
113+ view.doc.selection().cursor(),
122 count,
114 count,
123 );
115 );
124
116
125− // TODO: use a transaction
126− view.doc.state.selection = Selection::single(pos, pos);
117+ view.doc.set_selection(Selection::single(pos, pos));
127}
118}
128
119
129pub fn move_file_start(view: &mut View, _count: usize) {
120pub fn move_file_start(view: &mut View, _count: usize) {
130− // TODO: use a transaction
131− view.doc.state.selection = Selection::single(0, 0);
121+ view.doc.set_selection(Selection::single(0, 0));
132
122
133 view.doc.mode = Mode::Normal;
123 view.doc.mode = Mode::Normal;
134}
124}
135
125
136pub fn move_file_end(view: &mut View, _count: usize) {
126pub fn move_file_end(view: &mut View, _count: usize) {
137− // TODO: use a transaction
138 let text = &view.doc.text();
127 let text = &view.doc.text();
139 let last_line = text.line_to_char(text.len_lines().saturating_sub(2));
128 let last_line = text.line_to_char(text.len_lines().saturating_sub(2));
129+ view.doc
140− view.doc.state.selection = Selection::single(last_line, last_line);
130+ .set_selection(Selection::single(last_line, last_line));
141
131
142 view.doc.mode = Mode::Normal;
132 view.doc.mode = Mode::Normal;
143}
133}
144
134
145pub fn check_cursor_in_view(view: &mut View) -> bool {
135pub fn check_cursor_in_view(view: &mut View) -> bool {
146− let cursor = view.doc.state.selection().cursor();
136+ let cursor = view.doc.selection().cursor();
147 let line = view.doc.text().char_to_line(cursor);
137 let line = view.doc.text().char_to_line(cursor);
148 let document_end = view.first_line + view.size.1.saturating_sub(1) as usize;
138 let document_end = view.first_line + view.size.1.saturating_sub(1) as usize;
149
139
@@ -163,7 +153,7 @@ pub fn page_up(view: &mut View, _count: usize) {
163 if !check_cursor_in_view(view) {
153 if !check_cursor_in_view(view) {
164 let text = view.doc.text();
154 let text = view.doc.text();
165 let pos = text.line_to_char(view.last_line().saturating_sub(PADDING));
155 let pos = text.line_to_char(view.last_line().saturating_sub(PADDING));
166− view.doc.state.selection = Selection::single(pos, pos);
156+ view.doc.set_selection(Selection::single(pos, pos));
167 }
157 }
168}
158}
169
159
@@ -173,7 +163,7 @@ pub fn page_down(view: &mut View, _count: usize) {
173 if view.first_line < view.doc.text().len_lines() {
163 if view.first_line < view.doc.text().len_lines() {
174 let text = view.doc.text();
164 let text = view.doc.text();
175 let pos = text.line_to_char(view.first_line as usize);
165 let pos = text.line_to_char(view.first_line as usize);
176− view.doc.state.selection = Selection::single(pos, pos);
166+ view.doc.set_selection(Selection::single(pos, pos));
177 }
167 }
178}
168}
179
169
@@ -187,7 +177,7 @@ pub fn half_page_up(view: &mut View, _count: usize) {
187 if !check_cursor_in_view(view) {
177 if !check_cursor_in_view(view) {
188 let text = &view.doc.text();
178 let text = &view.doc.text();
189 let pos = text.line_to_char(view.last_line() - PADDING);
179 let pos = text.line_to_char(view.last_line() - PADDING);
190− view.doc.state.selection = Selection::single(pos, pos);
180+ view.doc.set_selection(Selection::single(pos, pos));
191 }
181 }
192}
182}
193
183
@@ -199,42 +189,41 @@ pub fn half_page_down(view: &mut View, _count: usize) {
199 if !check_cursor_in_view(view) {
189 if !check_cursor_in_view(view) {
200 let text = view.doc.text();
190 let text = view.doc.text();
201 let pos = text.line_to_char(view.first_line as usize);
191 let pos = text.line_to_char(view.first_line as usize);
202− view.doc.state.selection = Selection::single(pos, pos);
192+ view.doc.set_selection(Selection::single(pos, pos));
203 }
193 }
204}
194}
205// avoid select by default by having a visual mode switch that makes movements into selects
195// avoid select by default by having a visual mode switch that makes movements into selects
206
196
207pub fn extend_char_left(view: &mut View, count: usize) {
197pub fn extend_char_left(view: &mut View, count: usize) {
208− // TODO: use a transaction
209 let selection =
198 let selection =
210 view.doc
199 view.doc
211 .state
200 .state
212 .extend_selection(Direction::Backward, Granularity::Character, count);
201 .extend_selection(Direction::Backward, Granularity::Character, count);
213− view.doc.state.selection = selection;
202+ view.doc.set_selection(selection);
214}
203}
215
204
216pub fn extend_char_right(view: &mut View, count: usize) {
205pub fn extend_char_right(view: &mut View, count: usize) {
217− // TODO: use a transaction
218− view.doc.state.selection =
206+ let selection =
219 view.doc
207 view.doc
220 .state
208 .state
221 .extend_selection(Direction::Forward, Granularity::Character, count);
209 .extend_selection(Direction::Forward, Granularity::Character, count);
210+ view.doc.set_selection(selection);
222}
211}
223
212
224pub fn extend_line_up(view: &mut View, count: usize) {
213pub fn extend_line_up(view: &mut View, count: usize) {
225− // TODO: use a transaction
226− view.doc.state.selection =
214+ let selection = view
227− view.doc
215+ .doc
228− .state
216+ .state
229− .extend_selection(Direction::Backward, Granularity::Line, count);
217+ .extend_selection(Direction::Backward, Granularity::Line, count);
218+ view.doc.set_selection(selection);
230}
219}
231
220
232pub fn extend_line_down(view: &mut View, count: usize) {
221pub fn extend_line_down(view: &mut View, count: usize) {
233− // TODO: use a transaction
234− view.doc.state.selection =
222+ let selection = view
235− view.doc
223+ .doc
236− .state
224+ .state
237− .extend_selection(Direction::Forward, Granularity::Line, count);
225+ .extend_selection(Direction::Forward, Granularity::Line, count);
226+ view.doc.set_selection(selection);
238}
227}
239
228
240pub fn split_selection_on_newline(view: &mut View, _count: usize) {
229pub fn split_selection_on_newline(view: &mut View, _count: usize) {
@@ -242,20 +231,19 @@ pub fn split_selection_on_newline(view: &mut View, _count: usize) {
242 // only compile the regex once
231 // only compile the regex once
243 #[allow(clippy::trivial_regex)]
232 #[allow(clippy::trivial_regex)]
244 static REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"\n").unwrap());
233 static REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"\n").unwrap());
245− // TODO: use a transaction
246− view.doc.state.selection = selection::split_on_matches(text, view.doc.state.selection(), &REGEX)
234+ let selection = selection::split_on_matches(text, view.doc.selection(), &REGEX);
235+ view.doc.set_selection(selection);
247}
236}
248
237
249pub fn select_line(view: &mut View, _count: usize) {
238pub fn select_line(view: &mut View, _count: usize) {
250 // TODO: count
239 // TODO: count
251− let pos = view.doc.state.selection().primary();
240+ let pos = view.doc.selection().primary();
252 let text = view.doc.text();
241 let text = view.doc.text();
253 let line = text.char_to_line(pos.head);
242 let line = text.char_to_line(pos.head);
254 let start = text.line_to_char(line);
243 let start = text.line_to_char(line);
255 let end = text.line_to_char(line + 1).saturating_sub(1);
244 let end = text.line_to_char(line + 1).saturating_sub(1);
256
245
257− // TODO: use a transaction
258− view.doc.state.selection = Selection::single(start, end);
246+ view.doc.set_selection(Selection::single(start, end));
259}
247}
260
248
261pub fn delete_selection(view: &mut View, _count: usize) {
249pub fn delete_selection(view: &mut View, _count: usize) {
@@ -273,19 +261,21 @@ pub fn change_selection(view: &mut View, count: usize) {
273}
261}
274
262
275pub fn collapse_selection(view: &mut View, _count: usize) {
263pub fn collapse_selection(view: &mut View, _count: usize) {
276− view.doc.state.selection = view
264+ let selection = view
277 .doc
265 .doc
278− .state
279− .selection
266+ .selection()
280− .transform(|range| Range::new(range.head, range.head))
267+ .transform(|range| Range::new(range.head, range.head));
268+
269+ view.doc.set_selection(selection);
281}
270}
282
271
283pub fn flip_selections(view: &mut View, _count: usize) {
272pub fn flip_selections(view: &mut View, _count: usize) {
284− view.doc.state.selection = view
273+ let selection = view
285 .doc
274 .doc
286− .state
287− .selection
275+ .selection()
288− .transform(|range| Range::new(range.head, range.anchor))
276+ .transform(|range| Range::new(range.head, range.anchor));
277+
278+ view.doc.set_selection(selection);
289}
279}
290
280
291fn enter_insert_mode(view: &mut View) {
281fn enter_insert_mode(view: &mut View) {
@@ -297,11 +287,11 @@ fn enter_insert_mode(view: &mut View) {
297pub fn insert_mode(view: &mut View, _count: usize) {
287pub fn insert_mode(view: &mut View, _count: usize) {
298 enter_insert_mode(view);
288 enter_insert_mode(view);
299
289
300− view.doc.state.selection = view
290+ let selection = view
301 .doc
291 .doc
302− .state
303− .selection
292+ .selection()
304− .transform(|range| Range::new(range.to(), range.from()))
293+ .transform(|range| Range::new(range.to(), range.from()));
294+ view.doc.set_selection(selection);
305}
295}
306
296
307// inserts at the end of each selection
297// inserts at the end of each selection
@@ -311,13 +301,14 @@ pub fn append_mode(view: &mut View, _count: usize) {
311
301
312 // TODO: as transaction
302 // TODO: as transaction
313 let text = &view.doc.text().slice(..);
303 let text = &view.doc.text().slice(..);
314− view.doc.state.selection = view.doc.state.selection.transform(|range| {
304+ let selection = view.doc.selection().transform(|range| {
315 // TODO: to() + next char
305 // TODO: to() + next char
316 Range::new(
306 Range::new(
317 range.from(),
307 range.from(),
318 graphemes::next_grapheme_boundary(text, range.to()),
308 graphemes::next_grapheme_boundary(text, range.to()),
319 )
309 )
320− })
310+ });
311+ view.doc.set_selection(selection);
321}
312}
322
313
323// TODO: I, A, o and O can share a lot of the primitives.
314// TODO: I, A, o and O can share a lot of the primitives.
@@ -402,7 +393,7 @@ fn append_changes_to_history(view: &mut View) {
402 let changes = std::mem::replace(&mut view.doc.changes, new_changeset);
393 let changes = std::mem::replace(&mut view.doc.changes, new_changeset);
403 // Instead of doing this messy merge we could always commit, and based on transaction
394 // Instead of doing this messy merge we could always commit, and based on transaction
404 // annotations either add a new layer or compose into the previous one.
395 // annotations either add a new layer or compose into the previous one.
405− let transaction = Transaction::from(changes).with_selection(view.doc.state.selection().clone());
396+ let transaction = Transaction::from(changes).with_selection(view.doc.selection().clone());
406
397
407 // increment document version
398 // increment document version
408 // TODO: needs to happen on undo/redo too
399 // TODO: needs to happen on undo/redo too
@@ -424,12 +415,13 @@ pub fn normal_mode(view: &mut View, _count: usize) {
424 // if leaving append mode, move cursor back by 1
415 // if leaving append mode, move cursor back by 1
425 if view.doc.restore_cursor {
416 if view.doc.restore_cursor {
426 let text = &view.doc.text().slice(..);
417 let text = &view.doc.text().slice(..);
427− view.doc.state.selection = view.doc.state.selection.transform(|range| {
418+ let selection = view.doc.selection().transform(|range| {
428 Range::new(
419 Range::new(
429 range.from(),
420 range.from(),
430 graphemes::prev_grapheme_boundary(text, range.to()),
421 graphemes::prev_grapheme_boundary(text, range.to()),
431 )
422 )
432 });
423 });
424+ view.doc.set_selection(selection);
433
425
434 view.doc.restore_cursor = false;
426 view.doc.restore_cursor = false;
435 }
427 }
@@ -587,7 +579,7 @@ fn get_lines(view: &View) -> Vec<usize> {
587 let mut lines = Vec::new();
579 let mut lines = Vec::new();
588
580
589 // Get all line numbers
581 // Get all line numbers
590− for range in view.doc.state.selection.ranges() {
582+ for range in view.doc.selection().ranges() {
591 let start = view.doc.text().char_to_line(range.from());
583 let start = view.doc.text().char_to_line(range.from());
592 let end = view.doc.text().char_to_line(range.to());
584 let end = view.doc.text().char_to_line(range.to());
593
585
helix-view/src/document.rs+11
@@ -108,6 +108,11 @@ impl Document {
108 };
108 };
109 }
109 }
110
110
111+ pub fn set_selection(&mut self, selection: Selection) {
112+ // TODO: use a transaction?
113+ self.state.selection = selection;
114+ }
115+
111 pub fn apply(&mut self, transaction: &Transaction) -> bool {
116 pub fn apply(&mut self, transaction: &Transaction) -> bool {
112 let old_doc = self.text().clone();
117 let old_doc = self.text().clone();
113
118
@@ -119,6 +124,8 @@ impl Document {
119 changes.compose(transaction.changes().clone()).unwrap()
124 changes.compose(transaction.changes().clone()).unwrap()
120 });
125 });
121
126
127+ // TODO: when composing, replace transaction.selection too
128+
122 // update tree-sitter syntax tree
129 // update tree-sitter syntax tree
123 if let Some(syntax) = &mut self.syntax {
130 if let Some(syntax) = &mut self.syntax {
124 // TODO: no unwrap
131 // TODO: no unwrap
@@ -150,6 +157,10 @@ impl Document {
150 &self.state.doc
157 &self.state.doc
151 }
158 }
152
159
160+ pub fn selection(&self) -> &Selection {
161+ &self.state.selection
162+ }
163+
153 // pub fn slice<R>(&self, range: R) -> RopeSlice where R: RangeBounds {
164 // pub fn slice<R>(&self, range: R) -> RopeSlice where R: RangeBounds {
154 // self.state.doc.slice
165 // self.state.doc.slice
155 // }
166 // }