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