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