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