1use super::{
2 buffer, movement, Anchor, Bias, Buffer, BufferElement, DisplayMap, DisplayPoint, Point,
3 ToOffset, ToPoint,
4};
5use crate::{settings::Settings, watch, workspace};
6use anyhow::Result;
7use gpui::{
8 executor::BackgroundTask, fonts::Properties as FontProperties, keymap::Binding, text_layout,
9 App, AppContext, Element, ElementBox, Entity, FontCache, ModelHandle, MutableAppContext, View,
10 ViewContext, WeakViewHandle,
11};
12use gpui::{geometry::vector::Vector2F, TextLayoutCache};
13use parking_lot::Mutex;
14use smallvec::SmallVec;
15use smol::Timer;
16use std::{
17 cmp::{self, Ordering},
18 fmt::Write,
19 mem,
20 ops::Range,
21 sync::Arc,
22 time::Duration,
23};
24
25const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
26
27pub fn init(app: &mut App) {
28 app.add_bindings(vec![
29 Binding::new("backspace", "buffer:backspace", Some("BufferView")),
30 Binding::new("enter", "buffer:newline", Some("BufferView")),
31 Binding::new("up", "buffer:move_up", Some("BufferView")),
32 Binding::new("down", "buffer:move_down", Some("BufferView")),
33 Binding::new("left", "buffer:move_left", Some("BufferView")),
34 Binding::new("right", "buffer:move_right", Some("BufferView")),
35 Binding::new("shift-up", "buffer:select_up", Some("BufferView")),
36 Binding::new("shift-down", "buffer:select_down", Some("BufferView")),
37 Binding::new("shift-left", "buffer:select_left", Some("BufferView")),
38 Binding::new("shift-right", "buffer:select_right", Some("BufferView")),
39 Binding::new("pageup", "buffer:page_up", Some("BufferView")),
40 Binding::new("pagedown", "buffer:page_down", Some("BufferView")),
41 Binding::new("alt-cmd-[", "buffer:fold", Some("BufferView")),
42 Binding::new("alt-cmd-]", "buffer:unfold", Some("BufferView")),
43 Binding::new(
44 "alt-cmd-f",
45 "buffer:fold_selected_ranges",
46 Some("BufferView"),
47 ),
48 ]);
49
50 app.add_action("buffer:scroll", BufferView::scroll);
51 app.add_action("buffer:select", BufferView::select);
52 app.add_action("buffer:insert", BufferView::insert);
53 app.add_action("buffer:newline", BufferView::newline);
54 app.add_action("buffer:backspace", BufferView::backspace);
55 app.add_action("buffer:move_up", BufferView::move_up);
56 app.add_action("buffer:move_down", BufferView::move_down);
57 app.add_action("buffer:move_left", BufferView::move_left);
58 app.add_action("buffer:move_right", BufferView::move_right);
59 app.add_action("buffer:select_up", BufferView::select_up);
60 app.add_action("buffer:select_down", BufferView::select_down);
61 app.add_action("buffer:select_left", BufferView::select_left);
62 app.add_action("buffer:select_right", BufferView::select_right);
63 app.add_action("buffer:page_up", BufferView::page_up);
64 app.add_action("buffer:page_down", BufferView::page_down);
65 app.add_action("buffer:fold", BufferView::fold);
66 app.add_action("buffer:unfold", BufferView::unfold);
67 app.add_action(
68 "buffer:fold_selected_ranges",
69 BufferView::fold_selected_ranges,
70 );
71}
72
73pub enum SelectAction {
74 Begin {
75 position: DisplayPoint,
76 add: bool,
77 },
78 Update {
79 position: DisplayPoint,
80 scroll_position: Vector2F,
81 },
82 End,
83}
84
85// impl workspace::Item for Buffer {
86// type View = BufferView;
87
88// fn build_view(
89// buffer: ModelHandle<Self>,
90// settings: watch::Receiver<Settings>,
91// ctx: &mut ViewContext<Self::View>,
92// ) -> Self::View {
93// BufferView::for_buffer(buffer, settings, ctx)
94// }
95// }
96
97pub struct BufferView {
98 handle: WeakViewHandle<Self>,
99 buffer: ModelHandle<Buffer>,
100 display_map: ModelHandle<DisplayMap>,
101 selections: Vec<Selection>,
102 pending_selection: Option<Selection>,
103 scroll_position: Mutex<Vector2F>,
104 autoscroll_requested: Mutex<bool>,
105 settings: watch::Receiver<Settings>,
106 focused: bool,
107 cursors_visible: bool,
108 blink_epoch: usize,
109 blinking_paused: bool,
110 single_line: bool,
111}
112
113impl BufferView {
114 pub fn single_line(settings: watch::Receiver<Settings>, ctx: &mut ViewContext<Self>) -> Self {
115 let buffer = ctx.add_model(|_| Buffer::new(0, String::new()));
116 let mut view = Self::for_buffer(buffer, settings, ctx);
117 view.single_line = true;
118 view
119 }
120
121 pub fn for_buffer(
122 buffer: ModelHandle<Buffer>,
123 settings: watch::Receiver<Settings>,
124 ctx: &mut ViewContext<Self>,
125 ) -> Self {
126 settings.notify_view_on_change(ctx);
127
128 ctx.observe(&buffer, Self::on_buffer_changed);
129 ctx.subscribe_to_model(&buffer, Self::on_buffer_event);
130 let display_map = ctx.add_model(|ctx| {
131 DisplayMap::new(
132 buffer.clone(),
133 smol::block_on(settings.read()).tab_size,
134 ctx,
135 )
136 });
137 ctx.observe(&display_map, Self::on_display_map_changed);
138
139 let buffer_ref = buffer.as_ref(ctx);
140 Self {
141 handle: ctx.handle(),
142 buffer,
143 display_map,
144 selections: vec![Selection {
145 start: buffer_ref.anchor_before(0).unwrap(),
146 end: buffer_ref.anchor_before(0).unwrap(),
147 reversed: false,
148 goal_column: None,
149 }],
150 pending_selection: None,
151 scroll_position: Mutex::new(Vector2F::zero()),
152 autoscroll_requested: Mutex::new(false),
153 settings,
154 focused: false,
155 cursors_visible: false,
156 blink_epoch: 0,
157 blinking_paused: false,
158 single_line: false,
159 }
160 }
161
162 pub fn buffer(&self) -> &ModelHandle<Buffer> {
163 &self.buffer
164 }
165
166 pub fn is_gutter_visible(&self) -> bool {
167 !self.single_line
168 }
169
170 fn scroll(&mut self, scroll_position: &Vector2F, ctx: &mut ViewContext<Self>) {
171 *self.scroll_position.lock() = *scroll_position;
172 ctx.notify();
173 }
174
175 pub fn scroll_position(&self) -> Vector2F {
176 *self.scroll_position.lock()
177 }
178
179 pub fn clamp_scroll_left(&self, max: f32) {
180 let mut scroll_position = self.scroll_position.lock();
181 let scroll_left = scroll_position.x();
182 scroll_position.set_x(scroll_left.min(max));
183 }
184
185 pub fn autoscroll_vertically(
186 &self,
187 viewport_height: f32,
188 line_height: f32,
189 app: &AppContext,
190 ) -> bool {
191 let mut scroll_position = self.scroll_position.lock();
192 let scroll_top = scroll_position.y();
193 scroll_position.set_y(scroll_top.min(self.max_point(app).row().saturating_sub(1) as f32));
194
195 let mut autoscroll_requested = self.autoscroll_requested.lock();
196 if *autoscroll_requested {
197 *autoscroll_requested = false;
198 } else {
199 return false;
200 }
201
202 let map = self.display_map.as_ref(app);
203 let visible_lines = viewport_height / line_height;
204 let first_cursor_top = self
205 .selections
206 .first()
207 .unwrap()
208 .head()
209 .to_display_point(map, app)
210 .unwrap()
211 .row() as f32;
212 let last_cursor_bottom = self
213 .selections
214 .last()
215 .unwrap()
216 .head()
217 .to_display_point(map, app)
218 .unwrap()
219 .row() as f32
220 + 1.0;
221
222 let margin = ((visible_lines - (last_cursor_bottom - first_cursor_top)) / 2.0)
223 .floor()
224 .min(3.0);
225 if margin < 0.0 {
226 return false;
227 }
228
229 let target_top = (first_cursor_top - margin).max(0.0);
230 let target_bottom = last_cursor_bottom + margin;
231 let start_row = scroll_position.y();
232 let end_row = start_row + visible_lines;
233
234 if target_top < start_row {
235 scroll_position.set_y(target_top);
236 } else if target_bottom >= end_row {
237 scroll_position.set_y(target_bottom - visible_lines);
238 }
239
240 true
241 }
242
243 pub fn autoscroll_horizontally(
244 &self,
245 start_row: u32,
246 viewport_width: f32,
247 scroll_width: f32,
248 max_glyph_width: f32,
249 layouts: &[Arc<text_layout::Line>],
250 app: &AppContext,
251 ) {
252 let map = self.display_map.as_ref(app);
253
254 let mut target_left = std::f32::INFINITY;
255 let mut target_right = 0.0_f32;
256 for selection in &self.selections {
257 let head = selection.head().to_display_point(map, app).unwrap();
258 let start_column = head.column().saturating_sub(3);
259 let end_column = cmp::min(map.line_len(head.row(), app).unwrap(), head.column() + 3);
260 target_left = target_left
261 .min(layouts[(head.row() - start_row) as usize].x_for_index(start_column as usize));
262 target_right = target_right.max(
263 layouts[(head.row() - start_row) as usize].x_for_index(end_column as usize)
264 + max_glyph_width,
265 );
266 }
267 target_right = target_right.min(scroll_width);
268
269 if target_right - target_left > viewport_width {
270 return;
271 }
272
273 let mut scroll_position = self.scroll_position.lock();
274 let scroll_left = scroll_position.x() * max_glyph_width;
275 let scroll_right = scroll_left + viewport_width;
276
277 if target_left < scroll_left {
278 scroll_position.set_x(target_left / max_glyph_width);
279 } else if target_right > scroll_right {
280 scroll_position.set_x((target_right - viewport_width) / max_glyph_width);
281 }
282 }
283
284 fn select(&mut self, arg: &SelectAction, ctx: &mut ViewContext<Self>) {
285 match arg {
286 SelectAction::Begin { position, add } => self.begin_selection(*position, *add, ctx),
287 SelectAction::Update {
288 position,
289 scroll_position,
290 } => self.update_selection(*position, *scroll_position, ctx),
291 SelectAction::End => self.end_selection(ctx),
292 }
293 }
294
295 fn begin_selection(&mut self, position: DisplayPoint, add: bool, ctx: &mut ViewContext<Self>) {
296 if !self.focused {
297 ctx.focus_self();
298 ctx.emit(Event::Activate);
299 }
300
301 let display_map = self.display_map.as_ref(ctx);
302 let cursor = display_map
303 .anchor_before(position, Bias::Left, ctx.app())
304 .unwrap();
305 let selection = Selection {
306 start: cursor.clone(),
307 end: cursor,
308 reversed: false,
309 goal_column: None,
310 };
311
312 if !add {
313 self.selections.clear();
314 }
315 self.pending_selection = Some(selection);
316
317 ctx.notify();
318 }
319
320 fn update_selection(
321 &mut self,
322 position: DisplayPoint,
323 scroll_position: Vector2F,
324 ctx: &mut ViewContext<Self>,
325 ) {
326 let buffer = self.buffer.as_ref(ctx);
327 let map = self.display_map.as_ref(ctx);
328 let cursor = map.anchor_before(position, Bias::Left, ctx.app()).unwrap();
329 if let Some(selection) = self.pending_selection.as_mut() {
330 selection.set_head(buffer, cursor);
331 } else {
332 log::error!("update_selection dispatched with no pending selection");
333 return;
334 }
335
336 *self.scroll_position.lock() = scroll_position;
337
338 ctx.notify();
339 }
340
341 fn end_selection(&mut self, ctx: &mut ViewContext<Self>) {
342 if let Some(selection) = self.pending_selection.take() {
343 let ix = self.selection_insertion_index(&selection.start, ctx.app());
344 self.selections.insert(ix, selection);
345 self.merge_selections(ctx.app());
346 ctx.notify();
347 } else {
348 log::error!("end_selection dispatched with no pending selection");
349 }
350 }
351
352 pub fn is_selecting(&self) -> bool {
353 self.pending_selection.is_some()
354 }
355
356 #[cfg(test)]
357 fn select_ranges<'a, T>(&mut self, ranges: T, ctx: &mut ViewContext<Self>) -> Result<()>
358 where
359 T: IntoIterator<Item = &'a Range<DisplayPoint>>,
360 {
361 let buffer = self.buffer.as_ref(ctx);
362 let map = self.display_map.as_ref(ctx);
363 let mut selections = Vec::new();
364 for range in ranges {
365 selections.push(Selection {
366 start: map.anchor_after(range.start, Bias::Left, ctx.app())?,
367 end: map.anchor_before(range.end, Bias::Left, ctx.app())?,
368 reversed: false,
369 goal_column: None,
370 });
371 }
372 selections.sort_unstable_by(|a, b| a.start.cmp(&b.start, buffer).unwrap());
373 self.selections = selections;
374 self.merge_selections(ctx.app());
375 ctx.notify();
376 Ok(())
377 }
378
379 fn insert(&mut self, text: &String, ctx: &mut ViewContext<Self>) {
380 let buffer = self.buffer.as_ref(ctx);
381 let mut offset_ranges = SmallVec::<[Range<usize>; 32]>::new();
382 for selection in &self.selections {
383 let start = selection.start.to_offset(buffer).unwrap();
384 let end = selection.end.to_offset(buffer).unwrap();
385 offset_ranges.push(start..end);
386 }
387
388 self.buffer.update(ctx, |buffer, ctx| {
389 if let Err(error) = buffer.edit(offset_ranges.iter().cloned(), text.as_str(), Some(ctx))
390 {
391 log::error!("error inserting text: {}", error);
392 };
393 });
394
395 let buffer = self.buffer.as_ref(ctx);
396 let char_count = text.chars().count() as isize;
397 let mut delta = 0_isize;
398 self.selections = offset_ranges
399 .into_iter()
400 .map(|range| {
401 let start = range.start as isize;
402 let end = range.end as isize;
403 let anchor = buffer
404 .anchor_before((start + delta + char_count) as usize)
405 .unwrap();
406 let deleted_count = end - start;
407 delta += char_count - deleted_count;
408 Selection {
409 start: anchor.clone(),
410 end: anchor,
411 reversed: false,
412 goal_column: None,
413 }
414 })
415 .collect();
416
417 self.pause_cursor_blinking(ctx);
418 *self.autoscroll_requested.lock() = true;
419 }
420
421 fn newline(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
422 if self.single_line {
423 ctx.propagate_action();
424 } else {
425 self.insert(&"\n".into(), ctx);
426 }
427 }
428
429 pub fn backspace(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
430 let buffer = self.buffer.as_ref(ctx);
431 let map = self.display_map.as_ref(ctx);
432 for selection in &mut self.selections {
433 if selection.range(buffer).is_empty() {
434 let head = selection.head().to_display_point(map, ctx.app()).unwrap();
435 let cursor = map
436 .anchor_before(
437 movement::left(map, head, ctx.app()).unwrap(),
438 Bias::Left,
439 ctx.app(),
440 )
441 .unwrap();
442 selection.set_head(&buffer, cursor);
443 selection.goal_column = None;
444 }
445 }
446 self.changed_selections(ctx);
447 self.insert(&String::new(), ctx);
448 }
449
450 pub fn move_left(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
451 {
452 let app = ctx.app();
453 let map = self.display_map.as_ref(ctx);
454 for selection in &mut self.selections {
455 let start = selection.start.to_display_point(map, app).unwrap();
456 let end = selection.end.to_display_point(map, app).unwrap();
457
458 if start != end {
459 selection.end = selection.start.clone();
460 } else {
461 let cursor = map
462 .anchor_before(movement::left(map, start, app).unwrap(), Bias::Left, app)
463 .unwrap();
464 selection.start = cursor.clone();
465 selection.end = cursor;
466 }
467 selection.reversed = false;
468 selection.goal_column = None;
469 }
470 }
471 self.changed_selections(ctx);
472 }
473
474 pub fn select_left(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
475 {
476 let buffer = self.buffer.as_ref(ctx);
477 let map = self.display_map.as_ref(ctx);
478 for selection in &mut self.selections {
479 let head = selection.head().to_display_point(map, ctx.app()).unwrap();
480 let cursor = map
481 .anchor_before(
482 movement::left(map, head, ctx.app()).unwrap(),
483 Bias::Left,
484 ctx.app(),
485 )
486 .unwrap();
487 selection.set_head(&buffer, cursor);
488 selection.goal_column = None;
489 }
490 }
491 self.changed_selections(ctx);
492 }
493
494 pub fn move_right(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
495 {
496 let app = ctx.app();
497 let map = self.display_map.as_ref(app);
498 for selection in &mut self.selections {
499 let start = selection.start.to_display_point(map, app).unwrap();
500 let end = selection.end.to_display_point(map, app).unwrap();
501
502 if start != end {
503 selection.start = selection.end.clone();
504 } else {
505 let cursor = map
506 .anchor_before(movement::right(map, end, app).unwrap(), Bias::Right, app)
507 .unwrap();
508 selection.start = cursor.clone();
509 selection.end = cursor;
510 }
511 selection.reversed = false;
512 selection.goal_column = None;
513 }
514 }
515 self.changed_selections(ctx);
516 }
517
518 pub fn select_right(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
519 {
520 let buffer = self.buffer.as_ref(ctx);
521 let app = ctx.app();
522 let map = self.display_map.as_ref(app);
523 for selection in &mut self.selections {
524 let head = selection.head().to_display_point(map, ctx.app()).unwrap();
525 let cursor = map
526 .anchor_before(movement::right(map, head, app).unwrap(), Bias::Right, app)
527 .unwrap();
528 selection.set_head(&buffer, cursor);
529 selection.goal_column = None;
530 }
531 }
532 self.changed_selections(ctx);
533 }
534
535 pub fn move_up(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
536 if self.single_line {
537 ctx.propagate_action();
538 } else {
539 let app = ctx.app();
540 let map = self.display_map.as_ref(app);
541 for selection in &mut self.selections {
542 let start = selection.start.to_display_point(map, app).unwrap();
543 let end = selection.end.to_display_point(map, app).unwrap();
544 if start != end {
545 selection.goal_column = None;
546 }
547
548 let (start, goal_column) =
549 movement::up(map, start, selection.goal_column, app).unwrap();
550 let cursor = map.anchor_before(start, Bias::Left, app).unwrap();
551 selection.start = cursor.clone();
552 selection.end = cursor;
553 selection.goal_column = goal_column;
554 selection.reversed = false;
555 }
556 self.changed_selections(ctx);
557 }
558 }
559
560 pub fn select_up(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
561 if self.single_line {
562 ctx.propagate_action();
563 } else {
564 let app = ctx.app();
565 let buffer = self.buffer.as_ref(app);
566 let map = self.display_map.as_ref(app);
567 for selection in &mut self.selections {
568 let head = selection.head().to_display_point(map, app).unwrap();
569 let (head, goal_column) =
570 movement::up(map, head, selection.goal_column, app).unwrap();
571 selection.set_head(&buffer, map.anchor_before(head, Bias::Left, app).unwrap());
572 selection.goal_column = goal_column;
573 }
574 self.changed_selections(ctx);
575 }
576 }
577
578 pub fn move_down(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
579 if self.single_line {
580 ctx.propagate_action();
581 } else {
582 let app = ctx.app();
583 let map = self.display_map.as_ref(app);
584 for selection in &mut self.selections {
585 let start = selection.start.to_display_point(map, app).unwrap();
586 let end = selection.end.to_display_point(map, app).unwrap();
587 if start != end {
588 selection.goal_column = None;
589 }
590
591 let (start, goal_column) =
592 movement::down(map, end, selection.goal_column, app).unwrap();
593 let cursor = map.anchor_before(start, Bias::Right, app).unwrap();
594 selection.start = cursor.clone();
595 selection.end = cursor;
596 selection.goal_column = goal_column;
597 selection.reversed = false;
598 }
599 self.changed_selections(ctx);
600 }
601 }
602
603 pub fn select_down(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
604 if self.single_line {
605 ctx.propagate_action();
606 } else {
607 let app = ctx.app();
608 let buffer = self.buffer.as_ref(ctx);
609 let map = self.display_map.as_ref(ctx);
610 for selection in &mut self.selections {
611 let head = selection.head().to_display_point(map, app).unwrap();
612 let (head, goal_column) =
613 movement::down(map, head, selection.goal_column, app).unwrap();
614 selection.set_head(&buffer, map.anchor_before(head, Bias::Right, app).unwrap());
615 selection.goal_column = goal_column;
616 }
617 self.changed_selections(ctx);
618 }
619 }
620
621 pub fn changed_selections(&mut self, ctx: &mut ViewContext<Self>) {
622 self.merge_selections(ctx.app());
623 self.pause_cursor_blinking(ctx);
624 *self.autoscroll_requested.lock() = true;
625 ctx.notify();
626 }
627
628 fn merge_selections(&mut self, ctx: &AppContext) {
629 let buffer = self.buffer.as_ref(ctx);
630 let mut i = 1;
631 while i < self.selections.len() {
632 if self.selections[i - 1]
633 .end
634 .cmp(&self.selections[i].start, buffer)
635 .unwrap()
636 >= Ordering::Equal
637 {
638 let removed = self.selections.remove(i);
639 if removed
640 .start
641 .cmp(&self.selections[i - 1].start, buffer)
642 .unwrap()
643 < Ordering::Equal
644 {
645 self.selections[i - 1].start = removed.start;
646 }
647 if removed
648 .end
649 .cmp(&self.selections[i - 1].end, buffer)
650 .unwrap()
651 > Ordering::Equal
652 {
653 self.selections[i - 1].end = removed.end;
654 }
655 } else {
656 i += 1;
657 }
658 }
659 }
660
661 pub fn first_selection(&self, app: &AppContext) -> Range<DisplayPoint> {
662 self.selections
663 .first()
664 .unwrap()
665 .display_range(self.display_map.as_ref(app), app)
666 }
667
668 pub fn last_selection(&self, app: &AppContext) -> Range<DisplayPoint> {
669 self.selections
670 .last()
671 .unwrap()
672 .display_range(self.display_map.as_ref(app), app)
673 }
674
675 pub fn selections_in_range<'a>(
676 &'a self,
677 range: Range<DisplayPoint>,
678 app: &'a AppContext,
679 ) -> impl 'a + Iterator<Item = Range<DisplayPoint>> {
680 let map = self.display_map.as_ref(app);
681
682 let start = map.anchor_before(range.start, Bias::Left, app).unwrap();
683 let start_index = self.selection_insertion_index(&start, app);
684 let pending_selection = self.pending_selection.as_ref().and_then(|s| {
685 let selection_range = s.display_range(map, app);
686 if selection_range.start <= range.end || selection_range.end <= range.end {
687 Some(selection_range)
688 } else {
689 None
690 }
691 });
692 self.selections[start_index..]
693 .iter()
694 .map(move |s| s.display_range(map, app))
695 .take_while(move |r| r.start <= range.end || r.end <= range.end)
696 .chain(pending_selection)
697 }
698
699 fn selection_insertion_index(&self, start: &Anchor, app: &AppContext) -> usize {
700 let buffer = self.buffer.as_ref(app);
701
702 match self
703 .selections
704 .binary_search_by(|probe| probe.start.cmp(&start, buffer).unwrap())
705 {
706 Ok(index) => index,
707 Err(index) => {
708 if index > 0
709 && self.selections[index - 1].end.cmp(&start, buffer).unwrap()
710 == Ordering::Greater
711 {
712 index - 1
713 } else {
714 index
715 }
716 }
717 }
718 }
719
720 pub fn page_up(&mut self, _: &(), _: &mut ViewContext<Self>) {
721 log::info!("BufferView::page_up");
722 }
723
724 pub fn page_down(&mut self, _: &(), _: &mut ViewContext<Self>) {
725 log::info!("BufferView::page_down");
726 }
727
728 pub fn fold(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
729 use super::RangeExt;
730
731 let mut fold_ranges = Vec::new();
732
733 let app = ctx.app();
734 let map = self.display_map.as_ref(app);
735 for selection in &self.selections {
736 let (start, end) = selection.display_range(map, app).sorted();
737 let buffer_start_row = start.to_buffer_point(map, Bias::Left, app).unwrap().row;
738
739 for row in (0..=end.row()).rev() {
740 if self.is_line_foldable(row, app) && !map.is_line_folded(row) {
741 let fold_range = self.foldable_range_for_line(row, app).unwrap();
742 if fold_range.end.row >= buffer_start_row {
743 fold_ranges.push(fold_range);
744 if row <= start.row() {
745 break;
746 }
747 }
748 }
749 }
750 }
751
752 if !fold_ranges.is_empty() {
753 self.display_map.update(ctx, |map, ctx| {
754 map.fold(fold_ranges, ctx).unwrap();
755 });
756 *self.autoscroll_requested.lock() = true;
757 }
758 }
759
760 pub fn unfold(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
761 use super::RangeExt;
762
763 let app = ctx.app();
764 let map = self.display_map.as_ref(app);
765 let buffer = self.buffer.as_ref(app);
766 let ranges = self
767 .selections
768 .iter()
769 .map(|s| {
770 let (start, end) = s.display_range(map, app).sorted();
771 let mut start = start.to_buffer_point(map, Bias::Left, app).unwrap();
772 let mut end = end.to_buffer_point(map, Bias::Left, app).unwrap();
773 start.column = 0;
774 end.column = buffer.line_len(end.row).unwrap();
775 start..end
776 })
777 .collect::<Vec<_>>();
778
779 self.display_map.update(ctx, |map, ctx| {
780 map.unfold(ranges, ctx).unwrap();
781 });
782 *self.autoscroll_requested.lock() = true;
783 }
784
785 fn is_line_foldable(&self, display_row: u32, app: &AppContext) -> bool {
786 let max_point = self.max_point(app);
787 if display_row >= max_point.row() {
788 false
789 } else {
790 let (start_indent, is_blank) = self.line_indent(display_row, app).unwrap();
791 if is_blank {
792 false
793 } else {
794 for display_row in display_row + 1..=max_point.row() {
795 let (indent, is_blank) = self.line_indent(display_row, app).unwrap();
796 if !is_blank {
797 return indent > start_indent;
798 }
799 }
800 false
801 }
802 }
803 }
804
805 fn line_indent(&self, display_row: u32, app: &AppContext) -> Result<(usize, bool)> {
806 let mut indent = 0;
807 let mut is_blank = true;
808 for c in self
809 .display_map
810 .as_ref(app)
811 .chars_at(DisplayPoint::new(display_row, 0), app)?
812 {
813 if c == ' ' {
814 indent += 1;
815 } else {
816 is_blank = c == '\n';
817 break;
818 }
819 }
820 Ok((indent, is_blank))
821 }
822
823 fn foldable_range_for_line(&self, start_row: u32, app: &AppContext) -> Result<Range<Point>> {
824 let map = self.display_map.as_ref(app);
825 let max_point = self.max_point(app);
826
827 let (start_indent, _) = self.line_indent(start_row, app)?;
828 let start = DisplayPoint::new(start_row, self.line_len(start_row, app)?);
829 let mut end = None;
830 for row in start_row + 1..=max_point.row() {
831 let (indent, is_blank) = self.line_indent(row, app)?;
832 if !is_blank && indent <= start_indent {
833 end = Some(DisplayPoint::new(row - 1, self.line_len(row - 1, app)?));
834 break;
835 }
836 }
837
838 let end = end.unwrap_or(max_point);
839 return Ok(start.to_buffer_point(map, Bias::Left, app)?
840 ..end.to_buffer_point(map, Bias::Left, app)?);
841 }
842
843 pub fn fold_selected_ranges(&mut self, _: &(), ctx: &mut ViewContext<Self>) {
844 self.display_map.update(ctx, |map, ctx| {
845 let buffer = self.buffer.as_ref(ctx);
846 let ranges = self
847 .selections
848 .iter()
849 .map(|s| s.range(buffer))
850 .collect::<Vec<_>>();
851 map.fold(ranges, ctx).unwrap();
852 });
853 }
854
855 pub fn line(&self, display_row: u32, app: &AppContext) -> Result<String> {
856 self.display_map.as_ref(app).line(display_row, app)
857 }
858
859 pub fn line_len(&self, display_row: u32, app: &AppContext) -> Result<u32> {
860 self.display_map.as_ref(app).line_len(display_row, app)
861 }
862
863 pub fn rightmost_point(&self, app: &AppContext) -> DisplayPoint {
864 self.display_map.as_ref(app).rightmost_point()
865 }
866
867 pub fn max_point(&self, app: &AppContext) -> DisplayPoint {
868 self.display_map.as_ref(app).max_point(app)
869 }
870
871 pub fn text(&self, app: &AppContext) -> String {
872 self.display_map.as_ref(app).text(app)
873 }
874
875 pub fn font_size(&self) -> f32 {
876 smol::block_on(self.settings.read()).buffer_font_size
877 }
878
879 pub fn font_ascent(&self, font_cache: &FontCache) -> f32 {
880 let settings = smol::block_on(self.settings.read());
881 let font_id = font_cache.default_font(settings.buffer_font_family);
882 let ascent = font_cache.metric(font_id, |m| m.ascent);
883 font_cache.scale_metric(ascent, font_id, settings.buffer_font_size)
884 }
885
886 pub fn font_descent(&self, font_cache: &FontCache) -> f32 {
887 let settings = smol::block_on(self.settings.read());
888 let font_id = font_cache.default_font(settings.buffer_font_family);
889 let ascent = font_cache.metric(font_id, |m| m.descent);
890 font_cache.scale_metric(ascent, font_id, settings.buffer_font_size)
891 }
892
893 pub fn line_height(&self, font_cache: &FontCache) -> f32 {
894 let settings = smol::block_on(self.settings.read());
895 let font_id = font_cache.default_font(settings.buffer_font_family);
896 font_cache
897 .bounding_box(font_id, settings.buffer_font_size)
898 .y()
899 }
900
901 pub fn em_width(&self, font_cache: &FontCache) -> f32 {
902 let settings = smol::block_on(self.settings.read());
903 let font_id = font_cache.default_font(settings.buffer_font_family);
904 font_cache.em_width(font_id, settings.buffer_font_size)
905 }
906
907 // TODO: Can we make this not return a result?
908 pub fn max_line_number_width(
909 &self,
910 font_cache: &FontCache,
911 layout_cache: &TextLayoutCache,
912 app: &AppContext,
913 ) -> Result<f32> {
914 let settings = smol::block_on(self.settings.read());
915 let font_size = settings.buffer_font_size;
916 let font_id =
917 font_cache.select_font(settings.buffer_font_family, &FontProperties::new())?;
918 let digit_count = ((self.buffer.as_ref(app).max_point().row + 1) as f32)
919 .log10()
920 .floor() as usize
921 + 1;
922
923 Ok(layout_cache
924 .layout_str(
925 "1".repeat(digit_count).as_str(),
926 font_size,
927 &[(0..digit_count, font_id)],
928 )
929 .width)
930 }
931
932 pub fn layout_line_numbers(
933 &self,
934 viewport_height: f32,
935 font_cache: &FontCache,
936 layout_cache: &TextLayoutCache,
937 app: &AppContext,
938 ) -> Result<Vec<Arc<text_layout::Line>>> {
939 let display_map = self.display_map.as_ref(app);
940
941 let settings = smol::block_on(self.settings.read());
942 let font_size = settings.buffer_font_size;
943 let font_id =
944 font_cache.select_font(settings.buffer_font_family, &FontProperties::new())?;
945
946 let start_row = self.scroll_position().y() as usize;
947 let end_row = cmp::min(
948 self.max_point(app).row() as usize,
949 start_row + (viewport_height / self.line_height(font_cache)).ceil() as usize,
950 );
951 let line_count = end_row - start_row + 1;
952
953 let mut layouts = Vec::with_capacity(line_count);
954 let mut line_number = String::new();
955 for buffer_row in display_map.buffer_rows(start_row as u32)?.take(line_count) {
956 line_number.clear();
957 write!(&mut line_number, "{}", buffer_row + 1).unwrap();
958 layouts.push(layout_cache.layout_str(
959 &line_number,
960 font_size,
961 &[(0..line_number.len(), font_id)],
962 ));
963 }
964
965 Ok(layouts)
966 }
967
968 pub fn layout_lines(
969 &self,
970 mut rows: Range<u32>,
971 font_cache: &FontCache,
972 layout_cache: &TextLayoutCache,
973 app: &AppContext,
974 ) -> Result<Vec<Arc<text_layout::Line>>> {
975 let display_map = self.display_map.as_ref(app);
976
977 rows.end = cmp::min(rows.end, display_map.max_point(app).row() + 1);
978 if rows.start >= rows.end {
979 return Ok(Vec::new());
980 }
981
982 let settings = smol::block_on(self.settings.read());
983 let font_id =
984 font_cache.select_font(settings.buffer_font_family, &FontProperties::new())?;
985 let font_size = settings.buffer_font_size;
986
987 let mut layouts = Vec::with_capacity(rows.len());
988 let mut line = String::new();
989 let mut line_len = 0;
990 let mut row = rows.start;
991 let chars = display_map
992 .chars_at(DisplayPoint::new(rows.start, 0), app)
993 .unwrap();
994 for char in chars.chain(Some('\n')) {
995 if char == '\n' {
996 layouts.push(layout_cache.layout_str(&line, font_size, &[(0..line_len, font_id)]));
997 line.clear();
998 line_len = 0;
999 row += 1;
1000 if row == rows.end {
1001 break;
1002 }
1003 } else {
1004 line_len += 1;
1005 line.push(char);
1006 }
1007 }
1008
1009 Ok(layouts)
1010 }
1011
1012 pub fn layout_line(
1013 &self,
1014 row: u32,
1015 font_cache: &FontCache,
1016 layout_cache: &TextLayoutCache,
1017 app: &AppContext,
1018 ) -> Result<Arc<text_layout::Line>> {
1019 let settings = smol::block_on(self.settings.read());
1020 let font_id =
1021 font_cache.select_font(settings.buffer_font_family, &FontProperties::new())?;
1022
1023 let line = self.line(row, app)?;
1024
1025 Ok(layout_cache.layout_str(
1026 &line,
1027 settings.buffer_font_size,
1028 &[(0..self.line_len(row, app)? as usize, font_id)],
1029 ))
1030 }
1031
1032 fn next_blink_epoch(&mut self) -> usize {
1033 self.blink_epoch += 1;
1034 self.blink_epoch
1035 }
1036
1037 fn pause_cursor_blinking(&mut self, ctx: &mut ViewContext<Self>) {
1038 self.cursors_visible = true;
1039 ctx.notify();
1040
1041 let epoch = self.next_blink_epoch();
1042 ctx.spawn(
1043 async move {
1044 Timer::after(CURSOR_BLINK_INTERVAL).await;
1045 epoch
1046 },
1047 Self::resume_cursor_blinking,
1048 )
1049 .detach();
1050 }
1051
1052 fn resume_cursor_blinking(&mut self, epoch: usize, ctx: &mut ViewContext<Self>) {
1053 if epoch == self.blink_epoch {
1054 self.blinking_paused = false;
1055 self.blink_cursors(epoch, ctx);
1056 }
1057 }
1058
1059 fn blink_cursors(&mut self, epoch: usize, ctx: &mut ViewContext<Self>) {
1060 if epoch == self.blink_epoch && self.focused && !self.blinking_paused {
1061 self.cursors_visible = !self.cursors_visible;
1062 ctx.notify();
1063
1064 let epoch = self.next_blink_epoch();
1065 ctx.spawn(
1066 async move {
1067 Timer::after(CURSOR_BLINK_INTERVAL).await;
1068 epoch
1069 },
1070 Self::blink_cursors,
1071 )
1072 .detach();
1073 }
1074 }
1075
1076 pub fn cursors_visible(&self) -> bool {
1077 self.cursors_visible
1078 }
1079
1080 fn on_buffer_changed(&mut self, _: ModelHandle<Buffer>, ctx: &mut ViewContext<Self>) {
1081 ctx.notify();
1082 }
1083
1084 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, ctx: &mut ViewContext<Self>) {
1085 ctx.notify();
1086 }
1087
1088 fn on_buffer_event(
1089 &mut self,
1090 _: ModelHandle<Buffer>,
1091 event: &buffer::Event,
1092 ctx: &mut ViewContext<Self>,
1093 ) {
1094 match event {
1095 buffer::Event::Edited(_) => ctx.emit(Event::Edited),
1096 }
1097 }
1098}
1099
1100struct Selection {
1101 start: Anchor,
1102 end: Anchor,
1103 reversed: bool,
1104 goal_column: Option<u32>,
1105}
1106
1107pub enum Event {
1108 Activate,
1109 Edited,
1110 Blurred,
1111}
1112
1113impl Entity for BufferView {
1114 type Event = Event;
1115}
1116
1117impl View for BufferView {
1118 fn render<'a>(&self, app: &AppContext) -> ElementBox {
1119 BufferElement::new(self.handle.upgrade(app).unwrap()).boxed()
1120 }
1121
1122 fn ui_name() -> &'static str {
1123 "BufferView"
1124 }
1125
1126 fn on_focus(&mut self, ctx: &mut ViewContext<Self>) {
1127 self.focused = true;
1128 self.blink_cursors(self.blink_epoch, ctx);
1129 }
1130
1131 fn on_blur(&mut self, ctx: &mut ViewContext<Self>) {
1132 self.focused = false;
1133 self.cursors_visible = false;
1134 ctx.emit(Event::Blurred);
1135 ctx.notify();
1136 }
1137}
1138
1139impl workspace::Item for Buffer {
1140 type View = BufferView;
1141
1142 fn build_view(
1143 buffer: ModelHandle<Self>,
1144 settings: watch::Receiver<Settings>,
1145 ctx: &mut ViewContext<Self::View>,
1146 ) -> Self::View {
1147 BufferView::for_buffer(buffer, settings, ctx)
1148 }
1149}
1150
1151impl workspace::ItemView for BufferView {
1152 fn is_activate_event(event: &Self::Event) -> bool {
1153 match event {
1154 Event::Activate => true,
1155 _ => false,
1156 }
1157 }
1158
1159 fn title(&self, app: &AppContext) -> std::string::String {
1160 if let Some(path) = self.buffer.as_ref(app).path(app) {
1161 path.file_name()
1162 .expect("buffer's path is always to a file")
1163 .to_string_lossy()
1164 .into()
1165 } else {
1166 "untitled".into()
1167 }
1168 }
1169
1170 fn entry_id(&self, app: &AppContext) -> Option<(usize, usize)> {
1171 self.buffer.as_ref(app).entry_id()
1172 }
1173
1174 fn clone_on_split(&self, ctx: &mut ViewContext<Self>) -> Option<Self>
1175 where
1176 Self: Sized,
1177 {
1178 let clone = BufferView::for_buffer(self.buffer.clone(), self.settings.clone(), ctx);
1179 *clone.scroll_position.lock() = *self.scroll_position.lock();
1180 Some(clone)
1181 }
1182
1183 fn save(&self, ctx: &mut MutableAppContext) -> Option<BackgroundTask<Result<()>>> {
1184 self.buffer.update(ctx, |buffer, ctx| buffer.save(ctx))
1185 }
1186}
1187
1188impl Selection {
1189 fn head(&self) -> &Anchor {
1190 if self.reversed {
1191 &self.start
1192 } else {
1193 &self.end
1194 }
1195 }
1196
1197 fn set_head(&mut self, buffer: &Buffer, cursor: Anchor) {
1198 if cursor.cmp(self.tail(), buffer).unwrap() < Ordering::Equal {
1199 if !self.reversed {
1200 mem::swap(&mut self.start, &mut self.end);
1201 self.reversed = true;
1202 }
1203 self.start = cursor;
1204 } else {
1205 if self.reversed {
1206 mem::swap(&mut self.start, &mut self.end);
1207 self.reversed = false;
1208 }
1209 self.end = cursor;
1210 }
1211 }
1212
1213 fn tail(&self) -> &Anchor {
1214 if self.reversed {
1215 &self.end
1216 } else {
1217 &self.start
1218 }
1219 }
1220
1221 fn range(&self, buffer: &Buffer) -> Range<Point> {
1222 let start = self.start.to_point(buffer).unwrap();
1223 let end = self.end.to_point(buffer).unwrap();
1224 if self.reversed {
1225 end..start
1226 } else {
1227 start..end
1228 }
1229 }
1230
1231 fn display_range(&self, map: &DisplayMap, app: &AppContext) -> Range<DisplayPoint> {
1232 let start = self.start.to_display_point(map, app).unwrap();
1233 let end = self.end.to_display_point(map, app).unwrap();
1234 if self.reversed {
1235 end..start
1236 } else {
1237 start..end
1238 }
1239 }
1240}
1241
1242#[cfg(test)]
1243mod tests {
1244 use super::*;
1245 use crate::{editor::Point, settings, test::sample_text};
1246 use anyhow::Error;
1247 use unindent::Unindent;
1248
1249 #[test]
1250 fn test_selection_with_mouse() {
1251 App::test((), |mut app| async move {
1252 let buffer = app.add_model(|_| Buffer::new(0, "aaaaaa\nbbbbbb\ncccccc\ndddddd\n"));
1253 let settings = settings::channel(&app.font_cache()).unwrap().1;
1254 let (_, buffer_view) =
1255 app.add_window(|ctx| BufferView::for_buffer(buffer, settings, ctx));
1256
1257 buffer_view.update(&mut app, |view, ctx| {
1258 view.begin_selection(DisplayPoint::new(2, 2), false, ctx);
1259 });
1260
1261 buffer_view.read(&app, |view, app| {
1262 let selections = view
1263 .selections_in_range(DisplayPoint::zero()..view.max_point(app), app)
1264 .collect::<Vec<_>>();
1265 assert_eq!(
1266 selections,
1267 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
1268 );
1269 });
1270
1271 buffer_view.update(&mut app, |view, ctx| {
1272 view.update_selection(DisplayPoint::new(3, 3), Vector2F::zero(), ctx);
1273 });
1274
1275 buffer_view.read(&app, |view, app| {
1276 let selections = view
1277 .selections_in_range(DisplayPoint::zero()..view.max_point(app), app)
1278 .collect::<Vec<_>>();
1279 assert_eq!(
1280 selections,
1281 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
1282 );
1283 });
1284
1285 buffer_view.update(&mut app, |view, ctx| {
1286 view.update_selection(DisplayPoint::new(1, 1), Vector2F::zero(), ctx);
1287 });
1288
1289 buffer_view.read(&app, |view, app| {
1290 let selections = view
1291 .selections_in_range(DisplayPoint::zero()..view.max_point(app), app)
1292 .collect::<Vec<_>>();
1293 assert_eq!(
1294 selections,
1295 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
1296 );
1297 });
1298
1299 buffer_view.update(&mut app, |view, ctx| {
1300 view.end_selection(ctx);
1301 view.update_selection(DisplayPoint::new(3, 3), Vector2F::zero(), ctx);
1302 });
1303
1304 buffer_view.read(&app, |view, app| {
1305 let selections = view
1306 .selections_in_range(DisplayPoint::zero()..view.max_point(app), app)
1307 .collect::<Vec<_>>();
1308 assert_eq!(
1309 selections,
1310 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
1311 );
1312 });
1313
1314 buffer_view.update(&mut app, |view, ctx| {
1315 view.begin_selection(DisplayPoint::new(3, 3), true, ctx);
1316 view.update_selection(DisplayPoint::new(0, 0), Vector2F::zero(), ctx);
1317 });
1318
1319 buffer_view.read(&app, |view, app| {
1320 let selections = view
1321 .selections_in_range(DisplayPoint::zero()..view.max_point(app), app)
1322 .collect::<Vec<_>>();
1323 assert_eq!(
1324 selections,
1325 [
1326 DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
1327 DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
1328 ]
1329 );
1330 });
1331
1332 buffer_view.update(&mut app, |view, ctx| {
1333 view.end_selection(ctx);
1334 });
1335
1336 buffer_view.read(&app, |view, app| {
1337 let selections = view
1338 .selections_in_range(DisplayPoint::zero()..view.max_point(app), app)
1339 .collect::<Vec<_>>();
1340 assert_eq!(
1341 selections,
1342 [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
1343 );
1344 });
1345 });
1346 }
1347
1348 #[test]
1349 fn test_layout_line_numbers() -> Result<()> {
1350 App::test((), |mut app| async move {
1351 let layout_cache = TextLayoutCache::new(app.platform().fonts());
1352 let font_cache = app.font_cache();
1353
1354 let buffer = app.add_model(|_| Buffer::new(0, sample_text(6, 6)));
1355
1356 let settings = settings::channel(&font_cache).unwrap().1;
1357 let (_, view) =
1358 app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx));
1359
1360 view.read(&app, |view, app| {
1361 let layouts = view.layout_line_numbers(1000.0, &font_cache, &layout_cache, app)?;
1362 assert_eq!(layouts.len(), 6);
1363 Result::<()>::Ok(())
1364 })?;
1365
1366 Ok(())
1367 })
1368 }
1369
1370 #[test]
1371 fn test_fold() -> Result<()> {
1372 App::test((), |mut app| async move {
1373 let buffer = app.add_model(|_| {
1374 Buffer::new(
1375 0,
1376 "
1377 impl Foo {
1378 // Hello!
1379
1380 fn a() {
1381 1
1382 }
1383
1384 fn b() {
1385 2
1386 }
1387
1388 fn c() {
1389 3
1390 }
1391 }
1392 "
1393 .unindent(),
1394 )
1395 });
1396 let settings = settings::channel(&app.font_cache()).unwrap().1;
1397 let (_, view) =
1398 app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx));
1399
1400 view.update(&mut app, |view, ctx| {
1401 view.select_ranges(&[DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)], ctx)?;
1402 view.fold(&(), ctx);
1403 assert_eq!(
1404 view.text(ctx.app()),
1405 "
1406 impl Foo {
1407 // Hello!
1408
1409 fn a() {
1410 1
1411 }
1412
1413 fn b() {…
1414 }
1415
1416 fn c() {…
1417 }
1418 }
1419 "
1420 .unindent(),
1421 );
1422
1423 view.fold(&(), ctx);
1424 assert_eq!(
1425 view.text(ctx.app()),
1426 "
1427 impl Foo {…
1428 }
1429 "
1430 .unindent(),
1431 );
1432
1433 view.unfold(&(), ctx);
1434 assert_eq!(
1435 view.text(ctx.app()),
1436 "
1437 impl Foo {
1438 // Hello!
1439
1440 fn a() {
1441 1
1442 }
1443
1444 fn b() {…
1445 }
1446
1447 fn c() {…
1448 }
1449 }
1450 "
1451 .unindent(),
1452 );
1453
1454 view.unfold(&(), ctx);
1455 assert_eq!(view.text(ctx.app()), buffer.as_ref(ctx).text());
1456
1457 Ok::<(), Error>(())
1458 })?;
1459
1460 Ok(())
1461 })
1462 }
1463
1464 #[test]
1465 fn test_move_cursor() -> Result<()> {
1466 App::test((), |mut app| async move {
1467 let buffer = app.add_model(|_| Buffer::new(0, sample_text(6, 6)));
1468 let settings = settings::channel(&app.font_cache()).unwrap().1;
1469 let (_, view) =
1470 app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx));
1471
1472 buffer.update(&mut app, |buffer, ctx| {
1473 buffer.edit(
1474 vec![
1475 Point::new(1, 0)..Point::new(1, 0),
1476 Point::new(1, 1)..Point::new(1, 1),
1477 ],
1478 "\t",
1479 Some(ctx),
1480 )
1481 })?;
1482
1483 view.update(&mut app, |view, ctx| {
1484 view.move_down(&(), ctx);
1485 assert_eq!(
1486 view.selections(ctx.app()),
1487 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
1488 );
1489 view.move_right(&(), ctx);
1490 assert_eq!(
1491 view.selections(ctx.app()),
1492 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
1493 );
1494 Ok::<(), Error>(())
1495 })?;
1496
1497 Ok(())
1498 })
1499 }
1500
1501 #[test]
1502 fn test_backspace() -> Result<()> {
1503 App::test((), |mut app| async move {
1504 let buffer = app.add_model(|_| {
1505 Buffer::new(0, "one two three\nfour five six\nseven eight nine\nten\n")
1506 });
1507 let settings = settings::channel(&app.font_cache()).unwrap().1;
1508 let (_, view) =
1509 app.add_window(|ctx| BufferView::for_buffer(buffer.clone(), settings, ctx));
1510
1511 view.update(&mut app, |view, ctx| -> Result<()> {
1512 view.select_ranges(
1513 &[
1514 // an empty selection - the preceding character is deleted
1515 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
1516 // one character selected - it is deleted
1517 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
1518 // a line suffix selected - it is deleted
1519 DisplayPoint::new(2, 6)..DisplayPoint::new(3, 0),
1520 ],
1521 ctx,
1522 )?;
1523 view.backspace(&(), ctx);
1524 Ok(())
1525 })?;
1526
1527 buffer.read(&mut app, |buffer, _| -> Result<()> {
1528 assert_eq!(buffer.text(), "oe two three\nfou five six\nseven ten\n");
1529 Ok(())
1530 })?;
1531
1532 Ok(())
1533 })
1534 }
1535
1536 impl BufferView {
1537 fn selections(&self, app: &AppContext) -> Vec<Range<DisplayPoint>> {
1538 self.selections_in_range(DisplayPoint::zero()..self.max_point(app), app)
1539 .collect::<Vec<_>>()
1540 }
1541 }
1542}