1pub mod display_map;
2mod element;
3pub mod items;
4pub mod movement;
5mod multi_buffer;
6
7#[cfg(test)]
8mod test;
9
10use aho_corasick::AhoCorasick;
11use clock::ReplicaId;
12use collections::{HashMap, HashSet};
13pub use display_map::DisplayPoint;
14use display_map::*;
15pub use element::*;
16use gpui::{
17 action,
18 elements::*,
19 fonts::TextStyle,
20 geometry::vector::{vec2f, Vector2F},
21 keymap::Binding,
22 text_layout, AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle,
23 MutableAppContext, RenderContext, View, ViewContext, WeakModelHandle, WeakViewHandle,
24};
25use items::BufferItemHandle;
26use itertools::Itertools as _;
27use language::{
28 BracketPair, Buffer, Diagnostic, DiagnosticSeverity, Language, Point, Selection, SelectionGoal,
29 TransactionId,
30};
31pub use multi_buffer::{
32 Anchor, AnchorRangeExt, ExcerptId, ExcerptProperties, MultiBuffer, ToOffset, ToPoint,
33};
34use multi_buffer::{MultiBufferChunks, MultiBufferSnapshot};
35use postage::watch;
36use serde::{Deserialize, Serialize};
37use smallvec::SmallVec;
38use smol::Timer;
39use std::{
40 cmp,
41 iter::{self, FromIterator},
42 mem,
43 ops::{Deref, Range, RangeInclusive, Sub},
44 rc::Rc,
45 sync::Arc,
46 time::{Duration, Instant},
47};
48use sum_tree::Bias;
49use text::rope::TextDimension;
50use theme::{DiagnosticStyle, EditorStyle};
51use util::post_inc;
52use workspace::{Navigation, PathOpener, Workspace};
53
54const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
55const MAX_LINE_LEN: usize = 1024;
56
57action!(Cancel);
58action!(Backspace);
59action!(Delete);
60action!(Input, String);
61action!(Newline);
62action!(Tab);
63action!(Outdent);
64action!(DeleteLine);
65action!(DeleteToPreviousWordBoundary);
66action!(DeleteToNextWordBoundary);
67action!(DeleteToBeginningOfLine);
68action!(DeleteToEndOfLine);
69action!(CutToEndOfLine);
70action!(DuplicateLine);
71action!(MoveLineUp);
72action!(MoveLineDown);
73action!(Cut);
74action!(Copy);
75action!(Paste);
76action!(Undo);
77action!(Redo);
78action!(MoveUp);
79action!(MoveDown);
80action!(MoveLeft);
81action!(MoveRight);
82action!(MoveToPreviousWordBoundary);
83action!(MoveToNextWordBoundary);
84action!(MoveToBeginningOfLine);
85action!(MoveToEndOfLine);
86action!(MoveToBeginning);
87action!(MoveToEnd);
88action!(SelectUp);
89action!(SelectDown);
90action!(SelectLeft);
91action!(SelectRight);
92action!(SelectToPreviousWordBoundary);
93action!(SelectToNextWordBoundary);
94action!(SelectToBeginningOfLine, bool);
95action!(SelectToEndOfLine);
96action!(SelectToBeginning);
97action!(SelectToEnd);
98action!(SelectAll);
99action!(SelectLine);
100action!(SplitSelectionIntoLines);
101action!(AddSelectionAbove);
102action!(AddSelectionBelow);
103action!(SelectNext, bool);
104action!(ToggleComments);
105action!(SelectLargerSyntaxNode);
106action!(SelectSmallerSyntaxNode);
107action!(MoveToEnclosingBracket);
108action!(ShowNextDiagnostic);
109action!(PageUp);
110action!(PageDown);
111action!(Fold);
112action!(Unfold);
113action!(FoldSelectedRanges);
114action!(Scroll, Vector2F);
115action!(Select, SelectPhase);
116
117pub fn init(cx: &mut MutableAppContext, path_openers: &mut Vec<Box<dyn PathOpener>>) {
118 path_openers.push(Box::new(items::BufferOpener));
119 cx.add_bindings(vec![
120 Binding::new("escape", Cancel, Some("Editor")),
121 Binding::new("backspace", Backspace, Some("Editor")),
122 Binding::new("ctrl-h", Backspace, Some("Editor")),
123 Binding::new("delete", Delete, Some("Editor")),
124 Binding::new("ctrl-d", Delete, Some("Editor")),
125 Binding::new("enter", Newline, Some("Editor && mode == full")),
126 Binding::new(
127 "alt-enter",
128 Input("\n".into()),
129 Some("Editor && mode == auto_height"),
130 ),
131 Binding::new("tab", Tab, Some("Editor")),
132 Binding::new("shift-tab", Outdent, Some("Editor")),
133 Binding::new("ctrl-shift-K", DeleteLine, Some("Editor")),
134 Binding::new(
135 "alt-backspace",
136 DeleteToPreviousWordBoundary,
137 Some("Editor"),
138 ),
139 Binding::new("alt-h", DeleteToPreviousWordBoundary, Some("Editor")),
140 Binding::new("alt-delete", DeleteToNextWordBoundary, Some("Editor")),
141 Binding::new("alt-d", DeleteToNextWordBoundary, Some("Editor")),
142 Binding::new("cmd-backspace", DeleteToBeginningOfLine, Some("Editor")),
143 Binding::new("cmd-delete", DeleteToEndOfLine, Some("Editor")),
144 Binding::new("ctrl-k", CutToEndOfLine, Some("Editor")),
145 Binding::new("cmd-shift-D", DuplicateLine, Some("Editor")),
146 Binding::new("ctrl-cmd-up", MoveLineUp, Some("Editor")),
147 Binding::new("ctrl-cmd-down", MoveLineDown, Some("Editor")),
148 Binding::new("cmd-x", Cut, Some("Editor")),
149 Binding::new("cmd-c", Copy, Some("Editor")),
150 Binding::new("cmd-v", Paste, Some("Editor")),
151 Binding::new("cmd-z", Undo, Some("Editor")),
152 Binding::new("cmd-shift-Z", Redo, Some("Editor")),
153 Binding::new("up", MoveUp, Some("Editor")),
154 Binding::new("down", MoveDown, Some("Editor")),
155 Binding::new("left", MoveLeft, Some("Editor")),
156 Binding::new("right", MoveRight, Some("Editor")),
157 Binding::new("ctrl-p", MoveUp, Some("Editor")),
158 Binding::new("ctrl-n", MoveDown, Some("Editor")),
159 Binding::new("ctrl-b", MoveLeft, Some("Editor")),
160 Binding::new("ctrl-f", MoveRight, Some("Editor")),
161 Binding::new("alt-left", MoveToPreviousWordBoundary, Some("Editor")),
162 Binding::new("alt-b", MoveToPreviousWordBoundary, Some("Editor")),
163 Binding::new("alt-right", MoveToNextWordBoundary, Some("Editor")),
164 Binding::new("alt-f", MoveToNextWordBoundary, Some("Editor")),
165 Binding::new("cmd-left", MoveToBeginningOfLine, Some("Editor")),
166 Binding::new("ctrl-a", MoveToBeginningOfLine, Some("Editor")),
167 Binding::new("cmd-right", MoveToEndOfLine, Some("Editor")),
168 Binding::new("ctrl-e", MoveToEndOfLine, Some("Editor")),
169 Binding::new("cmd-up", MoveToBeginning, Some("Editor")),
170 Binding::new("cmd-down", MoveToEnd, Some("Editor")),
171 Binding::new("shift-up", SelectUp, Some("Editor")),
172 Binding::new("ctrl-shift-P", SelectUp, Some("Editor")),
173 Binding::new("shift-down", SelectDown, Some("Editor")),
174 Binding::new("ctrl-shift-N", SelectDown, Some("Editor")),
175 Binding::new("shift-left", SelectLeft, Some("Editor")),
176 Binding::new("ctrl-shift-B", SelectLeft, Some("Editor")),
177 Binding::new("shift-right", SelectRight, Some("Editor")),
178 Binding::new("ctrl-shift-F", SelectRight, Some("Editor")),
179 Binding::new(
180 "alt-shift-left",
181 SelectToPreviousWordBoundary,
182 Some("Editor"),
183 ),
184 Binding::new("alt-shift-B", SelectToPreviousWordBoundary, Some("Editor")),
185 Binding::new("alt-shift-right", SelectToNextWordBoundary, Some("Editor")),
186 Binding::new("alt-shift-F", SelectToNextWordBoundary, Some("Editor")),
187 Binding::new(
188 "cmd-shift-left",
189 SelectToBeginningOfLine(true),
190 Some("Editor"),
191 ),
192 Binding::new(
193 "ctrl-shift-A",
194 SelectToBeginningOfLine(true),
195 Some("Editor"),
196 ),
197 Binding::new("cmd-shift-right", SelectToEndOfLine, Some("Editor")),
198 Binding::new("ctrl-shift-E", SelectToEndOfLine, Some("Editor")),
199 Binding::new("cmd-shift-up", SelectToBeginning, Some("Editor")),
200 Binding::new("cmd-shift-down", SelectToEnd, Some("Editor")),
201 Binding::new("cmd-a", SelectAll, Some("Editor")),
202 Binding::new("cmd-l", SelectLine, Some("Editor")),
203 Binding::new("cmd-shift-L", SplitSelectionIntoLines, Some("Editor")),
204 Binding::new("cmd-alt-up", AddSelectionAbove, Some("Editor")),
205 Binding::new("cmd-ctrl-p", AddSelectionAbove, Some("Editor")),
206 Binding::new("cmd-alt-down", AddSelectionBelow, Some("Editor")),
207 Binding::new("cmd-ctrl-n", AddSelectionBelow, Some("Editor")),
208 Binding::new("cmd-d", SelectNext(false), Some("Editor")),
209 Binding::new("cmd-k cmd-d", SelectNext(true), Some("Editor")),
210 Binding::new("cmd-/", ToggleComments, Some("Editor")),
211 Binding::new("alt-up", SelectLargerSyntaxNode, Some("Editor")),
212 Binding::new("ctrl-w", SelectLargerSyntaxNode, Some("Editor")),
213 Binding::new("alt-down", SelectSmallerSyntaxNode, Some("Editor")),
214 Binding::new("ctrl-shift-W", SelectSmallerSyntaxNode, Some("Editor")),
215 Binding::new("f8", ShowNextDiagnostic, Some("Editor")),
216 Binding::new("ctrl-m", MoveToEnclosingBracket, Some("Editor")),
217 Binding::new("pageup", PageUp, Some("Editor")),
218 Binding::new("pagedown", PageDown, Some("Editor")),
219 Binding::new("alt-cmd-[", Fold, Some("Editor")),
220 Binding::new("alt-cmd-]", Unfold, Some("Editor")),
221 Binding::new("alt-cmd-f", FoldSelectedRanges, Some("Editor")),
222 ]);
223
224 cx.add_action(Editor::open_new);
225 cx.add_action(|this: &mut Editor, action: &Scroll, cx| this.set_scroll_position(action.0, cx));
226 cx.add_action(Editor::select);
227 cx.add_action(Editor::cancel);
228 cx.add_action(Editor::handle_input);
229 cx.add_action(Editor::newline);
230 cx.add_action(Editor::backspace);
231 cx.add_action(Editor::delete);
232 cx.add_action(Editor::tab);
233 cx.add_action(Editor::outdent);
234 cx.add_action(Editor::delete_line);
235 cx.add_action(Editor::delete_to_previous_word_boundary);
236 cx.add_action(Editor::delete_to_next_word_boundary);
237 cx.add_action(Editor::delete_to_beginning_of_line);
238 cx.add_action(Editor::delete_to_end_of_line);
239 cx.add_action(Editor::cut_to_end_of_line);
240 cx.add_action(Editor::duplicate_line);
241 cx.add_action(Editor::move_line_up);
242 cx.add_action(Editor::move_line_down);
243 cx.add_action(Editor::cut);
244 cx.add_action(Editor::copy);
245 cx.add_action(Editor::paste);
246 cx.add_action(Editor::undo);
247 cx.add_action(Editor::redo);
248 cx.add_action(Editor::move_up);
249 cx.add_action(Editor::move_down);
250 cx.add_action(Editor::move_left);
251 cx.add_action(Editor::move_right);
252 cx.add_action(Editor::move_to_previous_word_boundary);
253 cx.add_action(Editor::move_to_next_word_boundary);
254 cx.add_action(Editor::move_to_beginning_of_line);
255 cx.add_action(Editor::move_to_end_of_line);
256 cx.add_action(Editor::move_to_beginning);
257 cx.add_action(Editor::move_to_end);
258 cx.add_action(Editor::select_up);
259 cx.add_action(Editor::select_down);
260 cx.add_action(Editor::select_left);
261 cx.add_action(Editor::select_right);
262 cx.add_action(Editor::select_to_previous_word_boundary);
263 cx.add_action(Editor::select_to_next_word_boundary);
264 cx.add_action(Editor::select_to_beginning_of_line);
265 cx.add_action(Editor::select_to_end_of_line);
266 cx.add_action(Editor::select_to_beginning);
267 cx.add_action(Editor::select_to_end);
268 cx.add_action(Editor::select_all);
269 cx.add_action(Editor::select_line);
270 cx.add_action(Editor::split_selection_into_lines);
271 cx.add_action(Editor::add_selection_above);
272 cx.add_action(Editor::add_selection_below);
273 cx.add_action(Editor::select_next);
274 cx.add_action(Editor::toggle_comments);
275 cx.add_action(Editor::select_larger_syntax_node);
276 cx.add_action(Editor::select_smaller_syntax_node);
277 cx.add_action(Editor::move_to_enclosing_bracket);
278 cx.add_action(Editor::show_next_diagnostic);
279 cx.add_action(Editor::page_up);
280 cx.add_action(Editor::page_down);
281 cx.add_action(Editor::fold);
282 cx.add_action(Editor::unfold);
283 cx.add_action(Editor::fold_selected_ranges);
284}
285
286trait SelectionExt {
287 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
288 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
289 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
290 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
291 -> Range<u32>;
292}
293
294#[derive(Clone, Debug)]
295pub enum SelectPhase {
296 Begin {
297 position: DisplayPoint,
298 add: bool,
299 click_count: usize,
300 },
301 BeginColumnar {
302 position: DisplayPoint,
303 overshoot: u32,
304 },
305 Extend {
306 position: DisplayPoint,
307 click_count: usize,
308 },
309 Update {
310 position: DisplayPoint,
311 overshoot: u32,
312 scroll_position: Vector2F,
313 },
314 End,
315}
316
317#[derive(Clone, Debug)]
318enum SelectMode {
319 Character,
320 Word(Range<Anchor>),
321 Line(Range<Anchor>),
322 All,
323}
324
325#[derive(PartialEq, Eq)]
326pub enum Autoscroll {
327 Fit,
328 Center,
329 Newest,
330}
331
332#[derive(Copy, Clone, PartialEq, Eq)]
333pub enum EditorMode {
334 SingleLine,
335 AutoHeight { max_lines: usize },
336 Full,
337}
338
339#[derive(Clone)]
340pub struct EditorSettings {
341 pub tab_size: usize,
342 pub soft_wrap: SoftWrap,
343 pub style: EditorStyle,
344}
345
346#[derive(Clone)]
347pub enum SoftWrap {
348 None,
349 EditorWidth,
350 Column(u32),
351}
352
353pub type BuildSettings = Arc<dyn 'static + Send + Sync + Fn(&AppContext) -> EditorSettings>;
354
355pub struct Editor {
356 handle: WeakViewHandle<Self>,
357 buffer: ModelHandle<MultiBuffer>,
358 display_map: ModelHandle<DisplayMap>,
359 next_selection_id: usize,
360 selections: Arc<[Selection<Anchor>]>,
361 pending_selection: Option<PendingSelection>,
362 columnar_selection_tail: Option<Anchor>,
363 add_selections_state: Option<AddSelectionsState>,
364 select_next_state: Option<SelectNextState>,
365 selection_history:
366 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
367 autoclose_stack: Vec<BracketPairState>,
368 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
369 active_diagnostics: Option<ActiveDiagnosticGroup>,
370 scroll_position: Vector2F,
371 scroll_top_anchor: Option<Anchor>,
372 autoscroll_request: Option<Autoscroll>,
373 build_settings: BuildSettings,
374 focused: bool,
375 show_local_cursors: bool,
376 blink_epoch: usize,
377 blinking_paused: bool,
378 mode: EditorMode,
379 placeholder_text: Option<Arc<str>>,
380 highlighted_rows: Option<Range<u32>>,
381 navigation: Option<Rc<Navigation>>,
382}
383
384pub struct EditorSnapshot {
385 pub mode: EditorMode,
386 pub display_snapshot: DisplaySnapshot,
387 pub placeholder_text: Option<Arc<str>>,
388 is_focused: bool,
389 scroll_position: Vector2F,
390 scroll_top_anchor: Option<Anchor>,
391}
392
393struct PendingSelection {
394 selection: Selection<Anchor>,
395 mode: SelectMode,
396}
397
398struct AddSelectionsState {
399 above: bool,
400 stack: Vec<usize>,
401}
402
403struct SelectNextState {
404 query: AhoCorasick,
405 wordwise: bool,
406 done: bool,
407}
408
409#[derive(Debug)]
410struct BracketPairState {
411 ranges: Vec<Range<Anchor>>,
412 pair: BracketPair,
413}
414
415#[derive(Debug)]
416struct ActiveDiagnosticGroup {
417 primary_range: Range<Anchor>,
418 primary_message: String,
419 blocks: HashMap<BlockId, Diagnostic>,
420 is_valid: bool,
421}
422
423#[derive(Serialize, Deserialize)]
424struct ClipboardSelection {
425 len: usize,
426 is_entire_line: bool,
427}
428
429pub struct NavigationData {
430 anchor: Anchor,
431 offset: usize,
432}
433
434impl Editor {
435 pub fn single_line(build_settings: BuildSettings, cx: &mut ViewContext<Self>) -> Self {
436 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
437 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
438 let mut view = Self::for_buffer(buffer, build_settings, cx);
439 view.mode = EditorMode::SingleLine;
440 view
441 }
442
443 pub fn auto_height(
444 max_lines: usize,
445 build_settings: BuildSettings,
446 cx: &mut ViewContext<Self>,
447 ) -> Self {
448 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
449 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
450 let mut view = Self::for_buffer(buffer, build_settings, cx);
451 view.mode = EditorMode::AutoHeight { max_lines };
452 view
453 }
454
455 pub fn for_buffer(
456 buffer: ModelHandle<MultiBuffer>,
457 build_settings: BuildSettings,
458 cx: &mut ViewContext<Self>,
459 ) -> Self {
460 Self::new(buffer, build_settings, cx)
461 }
462
463 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
464 let mut clone = Self::new(self.buffer.clone(), self.build_settings.clone(), cx);
465 clone.scroll_position = self.scroll_position;
466 clone.scroll_top_anchor = self.scroll_top_anchor.clone();
467 clone.navigation = self.navigation.clone();
468 clone
469 }
470
471 pub fn new(
472 buffer: ModelHandle<MultiBuffer>,
473 build_settings: BuildSettings,
474 cx: &mut ViewContext<Self>,
475 ) -> Self {
476 let settings = build_settings(cx);
477 let display_map = cx.add_model(|cx| {
478 DisplayMap::new(
479 buffer.clone(),
480 settings.tab_size,
481 settings.style.text.font_id,
482 settings.style.text.font_size,
483 None,
484 cx,
485 )
486 });
487 cx.observe(&buffer, Self::on_buffer_changed).detach();
488 cx.subscribe(&buffer, Self::on_buffer_event).detach();
489 cx.observe(&display_map, Self::on_display_map_changed)
490 .detach();
491
492 let mut this = Self {
493 handle: cx.weak_handle(),
494 buffer,
495 display_map,
496 selections: Arc::from([]),
497 pending_selection: None,
498 columnar_selection_tail: None,
499 next_selection_id: 0,
500 add_selections_state: None,
501 select_next_state: None,
502 selection_history: Default::default(),
503 autoclose_stack: Default::default(),
504 select_larger_syntax_node_stack: Vec::new(),
505 active_diagnostics: None,
506 build_settings,
507 scroll_position: Vector2F::zero(),
508 scroll_top_anchor: None,
509 autoscroll_request: None,
510 focused: false,
511 show_local_cursors: false,
512 blink_epoch: 0,
513 blinking_paused: false,
514 mode: EditorMode::Full,
515 placeholder_text: None,
516 highlighted_rows: None,
517 navigation: None,
518 };
519 let selection = Selection {
520 id: post_inc(&mut this.next_selection_id),
521 start: 0,
522 end: 0,
523 reversed: false,
524 goal: SelectionGoal::None,
525 };
526 this.update_selections(vec![selection], None, cx);
527 this
528 }
529
530 pub fn open_new(
531 workspace: &mut Workspace,
532 _: &workspace::OpenNew,
533 cx: &mut ViewContext<Workspace>,
534 ) {
535 let buffer = cx.add_model(|cx| {
536 Buffer::new(0, "", cx).with_language(Some(language::PLAIN_TEXT.clone()), None, cx)
537 });
538 workspace.open_item(BufferItemHandle(buffer), cx);
539 }
540
541 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
542 self.buffer.read(cx).replica_id()
543 }
544
545 pub fn buffer(&self) -> &ModelHandle<MultiBuffer> {
546 &self.buffer
547 }
548
549 pub fn snapshot(&mut self, cx: &mut MutableAppContext) -> EditorSnapshot {
550 EditorSnapshot {
551 mode: self.mode,
552 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
553 scroll_position: self.scroll_position,
554 scroll_top_anchor: self.scroll_top_anchor.clone(),
555 placeholder_text: self.placeholder_text.clone(),
556 is_focused: self
557 .handle
558 .upgrade(cx)
559 .map_or(false, |handle| handle.is_focused(cx)),
560 }
561 }
562
563 pub fn language<'a>(&self, cx: &'a AppContext) -> Option<&'a Arc<Language>> {
564 self.buffer.read(cx).language(cx)
565 }
566
567 pub fn set_placeholder_text(
568 &mut self,
569 placeholder_text: impl Into<Arc<str>>,
570 cx: &mut ViewContext<Self>,
571 ) {
572 self.placeholder_text = Some(placeholder_text.into());
573 cx.notify();
574 }
575
576 pub fn set_scroll_position(&mut self, scroll_position: Vector2F, cx: &mut ViewContext<Self>) {
577 let map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
578
579 if scroll_position.y() == 0. {
580 self.scroll_top_anchor = None;
581 self.scroll_position = scroll_position;
582 } else {
583 let scroll_top_buffer_offset =
584 DisplayPoint::new(scroll_position.y() as u32, 0).to_offset(&map, Bias::Right);
585 let anchor = map
586 .buffer_snapshot
587 .anchor_at(scroll_top_buffer_offset, Bias::Right);
588 self.scroll_position = vec2f(
589 scroll_position.x(),
590 scroll_position.y() - anchor.to_display_point(&map).row() as f32,
591 );
592 self.scroll_top_anchor = Some(anchor);
593 }
594
595 cx.notify();
596 }
597
598 pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> Vector2F {
599 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
600 compute_scroll_position(&display_map, self.scroll_position, &self.scroll_top_anchor)
601 }
602
603 pub fn clamp_scroll_left(&mut self, max: f32) -> bool {
604 if max < self.scroll_position.x() {
605 self.scroll_position.set_x(max);
606 true
607 } else {
608 false
609 }
610 }
611
612 pub fn autoscroll_vertically(
613 &mut self,
614 viewport_height: f32,
615 line_height: f32,
616 cx: &mut ViewContext<Self>,
617 ) -> bool {
618 let visible_lines = viewport_height / line_height;
619 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
620 let mut scroll_position =
621 compute_scroll_position(&display_map, self.scroll_position, &self.scroll_top_anchor);
622 let max_scroll_top = if matches!(self.mode, EditorMode::AutoHeight { .. }) {
623 (display_map.max_point().row() as f32 - visible_lines + 1.).max(0.)
624 } else {
625 display_map.max_point().row().saturating_sub(1) as f32
626 };
627 if scroll_position.y() > max_scroll_top {
628 scroll_position.set_y(max_scroll_top);
629 self.set_scroll_position(scroll_position, cx);
630 }
631
632 let autoscroll = if let Some(autoscroll) = self.autoscroll_request.take() {
633 autoscroll
634 } else {
635 return false;
636 };
637
638 let first_cursor_top;
639 let last_cursor_bottom;
640 if autoscroll == Autoscroll::Newest {
641 let newest_selection = self.newest_selection::<Point>(&display_map.buffer_snapshot);
642 first_cursor_top = newest_selection.head().to_display_point(&display_map).row() as f32;
643 last_cursor_bottom = first_cursor_top + 1.;
644 } else {
645 let selections = self.local_selections::<Point>(cx);
646 first_cursor_top = selections
647 .first()
648 .unwrap()
649 .head()
650 .to_display_point(&display_map)
651 .row() as f32;
652 last_cursor_bottom = selections
653 .last()
654 .unwrap()
655 .head()
656 .to_display_point(&display_map)
657 .row() as f32
658 + 1.0;
659 }
660
661 let margin = if matches!(self.mode, EditorMode::AutoHeight { .. }) {
662 0.
663 } else {
664 ((visible_lines - (last_cursor_bottom - first_cursor_top)) / 2.0).floor()
665 };
666 if margin < 0.0 {
667 return false;
668 }
669
670 match autoscroll {
671 Autoscroll::Fit | Autoscroll::Newest => {
672 let margin = margin.min(3.0);
673 let target_top = (first_cursor_top - margin).max(0.0);
674 let target_bottom = last_cursor_bottom + margin;
675 let start_row = scroll_position.y();
676 let end_row = start_row + visible_lines;
677
678 if target_top < start_row {
679 scroll_position.set_y(target_top);
680 self.set_scroll_position(scroll_position, cx);
681 } else if target_bottom >= end_row {
682 scroll_position.set_y(target_bottom - visible_lines);
683 self.set_scroll_position(scroll_position, cx);
684 }
685 }
686 Autoscroll::Center => {
687 scroll_position.set_y((first_cursor_top - margin).max(0.0));
688 self.set_scroll_position(scroll_position, cx);
689 }
690 }
691
692 true
693 }
694
695 pub fn autoscroll_horizontally(
696 &mut self,
697 start_row: u32,
698 viewport_width: f32,
699 scroll_width: f32,
700 max_glyph_width: f32,
701 layouts: &[text_layout::Line],
702 cx: &mut ViewContext<Self>,
703 ) -> bool {
704 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
705 let selections = self.local_selections::<Point>(cx);
706 let mut target_left = std::f32::INFINITY;
707 let mut target_right = 0.0_f32;
708 for selection in selections {
709 let head = selection.head().to_display_point(&display_map);
710 if head.row() >= start_row && head.row() < start_row + layouts.len() as u32 {
711 let start_column = head.column().saturating_sub(3);
712 let end_column = cmp::min(display_map.line_len(head.row()), head.column() + 3);
713 target_left = target_left.min(
714 layouts[(head.row() - start_row) as usize].x_for_index(start_column as usize),
715 );
716 target_right = target_right.max(
717 layouts[(head.row() - start_row) as usize].x_for_index(end_column as usize)
718 + max_glyph_width,
719 );
720 }
721 }
722 target_right = target_right.min(scroll_width);
723
724 if target_right - target_left > viewport_width {
725 return false;
726 }
727
728 let scroll_left = self.scroll_position.x() * max_glyph_width;
729 let scroll_right = scroll_left + viewport_width;
730
731 if target_left < scroll_left {
732 self.scroll_position.set_x(target_left / max_glyph_width);
733 true
734 } else if target_right > scroll_right {
735 self.scroll_position
736 .set_x((target_right - viewport_width) / max_glyph_width);
737 true
738 } else {
739 false
740 }
741 }
742
743 fn select(&mut self, Select(phase): &Select, cx: &mut ViewContext<Self>) {
744 match phase {
745 SelectPhase::Begin {
746 position,
747 add,
748 click_count,
749 } => self.begin_selection(*position, *add, *click_count, cx),
750 SelectPhase::BeginColumnar {
751 position,
752 overshoot,
753 } => self.begin_columnar_selection(*position, *overshoot, cx),
754 SelectPhase::Extend {
755 position,
756 click_count,
757 } => self.extend_selection(*position, *click_count, cx),
758 SelectPhase::Update {
759 position,
760 overshoot,
761 scroll_position,
762 } => self.update_selection(*position, *overshoot, *scroll_position, cx),
763 SelectPhase::End => self.end_selection(cx),
764 }
765 }
766
767 fn extend_selection(
768 &mut self,
769 position: DisplayPoint,
770 click_count: usize,
771 cx: &mut ViewContext<Self>,
772 ) {
773 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
774 let tail = self
775 .newest_selection::<usize>(&display_map.buffer_snapshot)
776 .tail();
777 self.begin_selection(position, false, click_count, cx);
778
779 let position = position.to_offset(&display_map, Bias::Left);
780 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
781 let pending = self.pending_selection.as_mut().unwrap();
782
783 if position >= tail {
784 pending.selection.start = tail_anchor.clone();
785 } else {
786 pending.selection.end = tail_anchor.clone();
787 pending.selection.reversed = true;
788 }
789
790 match &mut pending.mode {
791 SelectMode::Word(range) | SelectMode::Line(range) => {
792 *range = tail_anchor.clone()..tail_anchor
793 }
794 _ => {}
795 }
796 }
797
798 fn begin_selection(
799 &mut self,
800 position: DisplayPoint,
801 add: bool,
802 click_count: usize,
803 cx: &mut ViewContext<Self>,
804 ) {
805 if !self.focused {
806 cx.focus_self();
807 cx.emit(Event::Activate);
808 }
809
810 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
811 let buffer = &display_map.buffer_snapshot;
812 let start;
813 let end;
814 let mode;
815 match click_count {
816 1 => {
817 start = buffer.anchor_before(position.to_point(&display_map));
818 end = start.clone();
819 mode = SelectMode::Character;
820 }
821 2 => {
822 let range = movement::surrounding_word(&display_map, position);
823 start = buffer.anchor_before(range.start.to_point(&display_map));
824 end = buffer.anchor_before(range.end.to_point(&display_map));
825 mode = SelectMode::Word(start.clone()..end.clone());
826 }
827 3 => {
828 let position = display_map.clip_point(position, Bias::Left);
829 let line_start = movement::line_beginning(&display_map, position, false);
830 let mut next_line_start = line_start.clone();
831 *next_line_start.row_mut() += 1;
832 *next_line_start.column_mut() = 0;
833 next_line_start = display_map.clip_point(next_line_start, Bias::Right);
834
835 start = buffer.anchor_before(line_start.to_point(&display_map));
836 end = buffer.anchor_before(next_line_start.to_point(&display_map));
837 mode = SelectMode::Line(start.clone()..end.clone());
838 }
839 _ => {
840 start = buffer.anchor_before(0);
841 end = buffer.anchor_before(buffer.len());
842 mode = SelectMode::All;
843 }
844 }
845
846 let selection = Selection {
847 id: post_inc(&mut self.next_selection_id),
848 start,
849 end,
850 reversed: false,
851 goal: SelectionGoal::None,
852 };
853
854 if !add {
855 self.update_selections::<usize>(Vec::new(), None, cx);
856 } else if click_count > 1 {
857 // Remove the newest selection since it was only added as part of this multi-click.
858 let newest_selection = self.newest_selection::<usize>(buffer);
859 let mut selections = self.local_selections(cx);
860 selections.retain(|selection| selection.id != newest_selection.id);
861 self.update_selections::<usize>(selections, None, cx)
862 }
863
864 self.pending_selection = Some(PendingSelection { selection, mode });
865
866 cx.notify();
867 }
868
869 fn begin_columnar_selection(
870 &mut self,
871 position: DisplayPoint,
872 overshoot: u32,
873 cx: &mut ViewContext<Self>,
874 ) {
875 if !self.focused {
876 cx.focus_self();
877 cx.emit(Event::Activate);
878 }
879
880 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
881 let tail = self
882 .newest_selection::<Point>(&display_map.buffer_snapshot)
883 .tail();
884 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
885
886 self.select_columns(
887 tail.to_display_point(&display_map),
888 position,
889 overshoot,
890 &display_map,
891 cx,
892 );
893 }
894
895 fn update_selection(
896 &mut self,
897 position: DisplayPoint,
898 overshoot: u32,
899 scroll_position: Vector2F,
900 cx: &mut ViewContext<Self>,
901 ) {
902 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
903
904 if let Some(tail) = self.columnar_selection_tail.as_ref() {
905 let tail = tail.to_display_point(&display_map);
906 self.select_columns(tail, position, overshoot, &display_map, cx);
907 } else if let Some(PendingSelection { selection, mode }) = self.pending_selection.as_mut() {
908 let buffer = self.buffer.read(cx).snapshot(cx);
909 let head;
910 let tail;
911 match mode {
912 SelectMode::Character => {
913 head = position.to_point(&display_map);
914 tail = selection.tail().to_point(&buffer);
915 }
916 SelectMode::Word(original_range) => {
917 let original_display_range = original_range.start.to_display_point(&display_map)
918 ..original_range.end.to_display_point(&display_map);
919 let original_buffer_range = original_display_range.start.to_point(&display_map)
920 ..original_display_range.end.to_point(&display_map);
921 if movement::is_inside_word(&display_map, position)
922 || original_display_range.contains(&position)
923 {
924 let word_range = movement::surrounding_word(&display_map, position);
925 if word_range.start < original_display_range.start {
926 head = word_range.start.to_point(&display_map);
927 } else {
928 head = word_range.end.to_point(&display_map);
929 }
930 } else {
931 head = position.to_point(&display_map);
932 }
933
934 if head <= original_buffer_range.start {
935 tail = original_buffer_range.end;
936 } else {
937 tail = original_buffer_range.start;
938 }
939 }
940 SelectMode::Line(original_range) => {
941 let original_display_range = original_range.start.to_display_point(&display_map)
942 ..original_range.end.to_display_point(&display_map);
943 let original_buffer_range = original_display_range.start.to_point(&display_map)
944 ..original_display_range.end.to_point(&display_map);
945 let line_start = movement::line_beginning(&display_map, position, false);
946 let mut next_line_start = line_start.clone();
947 *next_line_start.row_mut() += 1;
948 *next_line_start.column_mut() = 0;
949 next_line_start = display_map.clip_point(next_line_start, Bias::Right);
950
951 if line_start < original_display_range.start {
952 head = line_start.to_point(&display_map);
953 } else {
954 head = next_line_start.to_point(&display_map);
955 }
956
957 if head <= original_buffer_range.start {
958 tail = original_buffer_range.end;
959 } else {
960 tail = original_buffer_range.start;
961 }
962 }
963 SelectMode::All => {
964 return;
965 }
966 };
967
968 if head < tail {
969 selection.start = buffer.anchor_before(head);
970 selection.end = buffer.anchor_before(tail);
971 selection.reversed = true;
972 } else {
973 selection.start = buffer.anchor_before(tail);
974 selection.end = buffer.anchor_before(head);
975 selection.reversed = false;
976 }
977 } else {
978 log::error!("update_selection dispatched with no pending selection");
979 return;
980 }
981
982 self.set_scroll_position(scroll_position, cx);
983 cx.notify();
984 }
985
986 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
987 self.columnar_selection_tail.take();
988 if self.pending_selection.is_some() {
989 let selections = self.local_selections::<usize>(cx);
990 self.update_selections(selections, None, cx);
991 }
992 }
993
994 fn select_columns(
995 &mut self,
996 tail: DisplayPoint,
997 head: DisplayPoint,
998 overshoot: u32,
999 display_map: &DisplaySnapshot,
1000 cx: &mut ViewContext<Self>,
1001 ) {
1002 let start_row = cmp::min(tail.row(), head.row());
1003 let end_row = cmp::max(tail.row(), head.row());
1004 let start_column = cmp::min(tail.column(), head.column() + overshoot);
1005 let end_column = cmp::max(tail.column(), head.column() + overshoot);
1006 let reversed = start_column < tail.column();
1007
1008 let selections = (start_row..=end_row)
1009 .filter_map(|row| {
1010 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
1011 let start = display_map
1012 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
1013 .to_point(&display_map);
1014 let end = display_map
1015 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
1016 .to_point(&display_map);
1017 Some(Selection {
1018 id: post_inc(&mut self.next_selection_id),
1019 start,
1020 end,
1021 reversed,
1022 goal: SelectionGoal::None,
1023 })
1024 } else {
1025 None
1026 }
1027 })
1028 .collect::<Vec<_>>();
1029
1030 self.update_selections(selections, None, cx);
1031 cx.notify();
1032 }
1033
1034 pub fn is_selecting(&self) -> bool {
1035 self.pending_selection.is_some() || self.columnar_selection_tail.is_some()
1036 }
1037
1038 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
1039 if self.active_diagnostics.is_some() {
1040 self.dismiss_diagnostics(cx);
1041 } else if let Some(PendingSelection { selection, .. }) = self.pending_selection.take() {
1042 let buffer = self.buffer.read(cx).snapshot(cx);
1043 let selection = Selection {
1044 id: selection.id,
1045 start: selection.start.to_point(&buffer),
1046 end: selection.end.to_point(&buffer),
1047 reversed: selection.reversed,
1048 goal: selection.goal,
1049 };
1050 if self.local_selections::<Point>(cx).is_empty() {
1051 self.update_selections(vec![selection], Some(Autoscroll::Fit), cx);
1052 }
1053 } else {
1054 let buffer = self.buffer.read(cx).snapshot(cx);
1055 let mut oldest_selection = self.oldest_selection::<usize>(&buffer);
1056 if self.selection_count() == 1 {
1057 if oldest_selection.is_empty() {
1058 cx.propagate_action();
1059 return;
1060 }
1061
1062 oldest_selection.start = oldest_selection.head().clone();
1063 oldest_selection.end = oldest_selection.head().clone();
1064 }
1065 self.update_selections(vec![oldest_selection], Some(Autoscroll::Fit), cx);
1066 }
1067 }
1068
1069 #[cfg(any(test, feature = "test-support"))]
1070 pub fn selected_ranges<D: TextDimension + Ord + Sub<D, Output = D>>(
1071 &self,
1072 cx: &mut MutableAppContext,
1073 ) -> Vec<Range<D>> {
1074 self.local_selections::<D>(cx)
1075 .iter()
1076 .map(|s| {
1077 if s.reversed {
1078 s.end.clone()..s.start.clone()
1079 } else {
1080 s.start.clone()..s.end.clone()
1081 }
1082 })
1083 .collect()
1084 }
1085
1086 #[cfg(any(test, feature = "test-support"))]
1087 pub fn selected_display_ranges(&self, cx: &mut MutableAppContext) -> Vec<Range<DisplayPoint>> {
1088 let display_map = self
1089 .display_map
1090 .update(cx, |display_map, cx| display_map.snapshot(cx));
1091 self.selections
1092 .iter()
1093 .chain(
1094 self.pending_selection
1095 .as_ref()
1096 .map(|pending| &pending.selection),
1097 )
1098 .map(|s| {
1099 if s.reversed {
1100 s.end.to_display_point(&display_map)..s.start.to_display_point(&display_map)
1101 } else {
1102 s.start.to_display_point(&display_map)..s.end.to_display_point(&display_map)
1103 }
1104 })
1105 .collect()
1106 }
1107
1108 pub fn select_ranges<I, T>(
1109 &mut self,
1110 ranges: I,
1111 autoscroll: Option<Autoscroll>,
1112 cx: &mut ViewContext<Self>,
1113 ) where
1114 I: IntoIterator<Item = Range<T>>,
1115 T: ToOffset,
1116 {
1117 let buffer = self.buffer.read(cx).snapshot(cx);
1118 let selections = ranges
1119 .into_iter()
1120 .map(|range| {
1121 let mut start = range.start.to_offset(&buffer);
1122 let mut end = range.end.to_offset(&buffer);
1123 let reversed = if start > end {
1124 mem::swap(&mut start, &mut end);
1125 true
1126 } else {
1127 false
1128 };
1129 Selection {
1130 id: post_inc(&mut self.next_selection_id),
1131 start,
1132 end,
1133 reversed,
1134 goal: SelectionGoal::None,
1135 }
1136 })
1137 .collect::<Vec<_>>();
1138 self.update_selections(selections, autoscroll, cx);
1139 }
1140
1141 #[cfg(test)]
1142 fn select_display_ranges<'a, T>(&mut self, ranges: T, cx: &mut ViewContext<Self>)
1143 where
1144 T: IntoIterator<Item = &'a Range<DisplayPoint>>,
1145 {
1146 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1147 let selections = ranges
1148 .into_iter()
1149 .map(|range| {
1150 let mut start = range.start;
1151 let mut end = range.end;
1152 let reversed = if start > end {
1153 mem::swap(&mut start, &mut end);
1154 true
1155 } else {
1156 false
1157 };
1158 Selection {
1159 id: post_inc(&mut self.next_selection_id),
1160 start: start.to_point(&display_map),
1161 end: end.to_point(&display_map),
1162 reversed,
1163 goal: SelectionGoal::None,
1164 }
1165 })
1166 .collect();
1167 self.update_selections(selections, None, cx);
1168 }
1169
1170 pub fn handle_input(&mut self, action: &Input, cx: &mut ViewContext<Self>) {
1171 let text = action.0.as_ref();
1172 if !self.skip_autoclose_end(text, cx) {
1173 self.start_transaction(cx);
1174 self.insert(text, cx);
1175 self.autoclose_pairs(cx);
1176 self.end_transaction(cx);
1177 }
1178 }
1179
1180 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
1181 self.start_transaction(cx);
1182 let mut old_selections = SmallVec::<[_; 32]>::new();
1183 {
1184 let selections = self.local_selections::<Point>(cx);
1185 let buffer = self.buffer.read(cx).snapshot(cx);
1186 for selection in selections.iter() {
1187 let start_point = selection.start;
1188 let indent = buffer
1189 .indent_column_for_line(start_point.row)
1190 .min(start_point.column);
1191 let start = selection.start.to_offset(&buffer);
1192 let end = selection.end.to_offset(&buffer);
1193
1194 let mut insert_extra_newline = false;
1195 if let Some(language) = buffer.language() {
1196 let leading_whitespace_len = buffer
1197 .reversed_chars_at(start)
1198 .take_while(|c| c.is_whitespace() && *c != '\n')
1199 .map(|c| c.len_utf8())
1200 .sum::<usize>();
1201
1202 let trailing_whitespace_len = buffer
1203 .chars_at(end)
1204 .take_while(|c| c.is_whitespace() && *c != '\n')
1205 .map(|c| c.len_utf8())
1206 .sum::<usize>();
1207
1208 insert_extra_newline = language.brackets().iter().any(|pair| {
1209 let pair_start = pair.start.trim_end();
1210 let pair_end = pair.end.trim_start();
1211
1212 pair.newline
1213 && buffer.contains_str_at(end + trailing_whitespace_len, pair_end)
1214 && buffer.contains_str_at(
1215 (start - leading_whitespace_len).saturating_sub(pair_start.len()),
1216 pair_start,
1217 )
1218 });
1219 }
1220
1221 old_selections.push((selection.id, start..end, indent, insert_extra_newline));
1222 }
1223 }
1224
1225 self.buffer.update(cx, |buffer, cx| {
1226 let mut delta = 0_isize;
1227 let mut pending_edit: Option<PendingEdit> = None;
1228 for (_, range, indent, insert_extra_newline) in &old_selections {
1229 if pending_edit.as_ref().map_or(false, |pending| {
1230 pending.indent != *indent
1231 || pending.insert_extra_newline != *insert_extra_newline
1232 }) {
1233 let pending = pending_edit.take().unwrap();
1234 let mut new_text = String::with_capacity(1 + pending.indent as usize);
1235 new_text.push('\n');
1236 new_text.extend(iter::repeat(' ').take(pending.indent as usize));
1237 if pending.insert_extra_newline {
1238 new_text = new_text.repeat(2);
1239 }
1240 buffer.edit_with_autoindent(pending.ranges, new_text, cx);
1241 delta += pending.delta;
1242 }
1243
1244 let start = (range.start as isize + delta) as usize;
1245 let end = (range.end as isize + delta) as usize;
1246 let mut text_len = *indent as usize + 1;
1247 if *insert_extra_newline {
1248 text_len *= 2;
1249 }
1250
1251 let pending = pending_edit.get_or_insert_with(Default::default);
1252 pending.delta += text_len as isize - (end - start) as isize;
1253 pending.indent = *indent;
1254 pending.insert_extra_newline = *insert_extra_newline;
1255 pending.ranges.push(start..end);
1256 }
1257
1258 let pending = pending_edit.unwrap();
1259 let mut new_text = String::with_capacity(1 + pending.indent as usize);
1260 new_text.push('\n');
1261 new_text.extend(iter::repeat(' ').take(pending.indent as usize));
1262 if pending.insert_extra_newline {
1263 new_text = new_text.repeat(2);
1264 }
1265 buffer.edit_with_autoindent(pending.ranges, new_text, cx);
1266
1267 let buffer = buffer.read(cx);
1268 self.selections = self
1269 .selections
1270 .iter()
1271 .cloned()
1272 .zip(old_selections)
1273 .map(|(mut new_selection, (_, _, _, insert_extra_newline))| {
1274 if insert_extra_newline {
1275 let mut cursor = new_selection.start.to_point(&buffer);
1276 cursor.row -= 1;
1277 cursor.column = buffer.line_len(cursor.row);
1278 let anchor = buffer.anchor_after(cursor);
1279 new_selection.start = anchor.clone();
1280 new_selection.end = anchor;
1281 }
1282 new_selection
1283 })
1284 .collect();
1285 });
1286
1287 self.request_autoscroll(Autoscroll::Fit, cx);
1288 self.end_transaction(cx);
1289
1290 #[derive(Default)]
1291 struct PendingEdit {
1292 indent: u32,
1293 insert_extra_newline: bool,
1294 delta: isize,
1295 ranges: SmallVec<[Range<usize>; 32]>,
1296 }
1297 }
1298
1299 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
1300 self.start_transaction(cx);
1301 let old_selections = self.local_selections::<usize>(cx);
1302 self.buffer.update(cx, |buffer, cx| {
1303 let edit_ranges = old_selections.iter().map(|s| s.start..s.end);
1304 buffer.edit_with_autoindent(edit_ranges, text, cx);
1305 });
1306
1307 let selections = self.local_selections::<usize>(cx);
1308 self.update_selections(selections, Some(Autoscroll::Fit), cx);
1309 self.end_transaction(cx);
1310 }
1311
1312 fn autoclose_pairs(&mut self, cx: &mut ViewContext<Self>) {
1313 let selections = self.local_selections::<usize>(cx);
1314 let mut bracket_pair_state = None;
1315 let mut new_selections = None;
1316 self.buffer.update(cx, |buffer, cx| {
1317 let mut snapshot = buffer.snapshot(cx);
1318 let left_biased_selections = selections
1319 .iter()
1320 .map(|selection| Selection {
1321 id: selection.id,
1322 start: snapshot.anchor_before(selection.start),
1323 end: snapshot.anchor_before(selection.end),
1324 reversed: selection.reversed,
1325 goal: selection.goal,
1326 })
1327 .collect::<Vec<_>>();
1328
1329 let autoclose_pair = snapshot.language().and_then(|language| {
1330 let first_selection_start = selections.first().unwrap().start;
1331 let pair = language.brackets().iter().find(|pair| {
1332 snapshot.contains_str_at(
1333 first_selection_start.saturating_sub(pair.start.len()),
1334 &pair.start,
1335 )
1336 });
1337 pair.and_then(|pair| {
1338 let should_autoclose = selections[1..].iter().all(|selection| {
1339 snapshot.contains_str_at(
1340 selection.start.saturating_sub(pair.start.len()),
1341 &pair.start,
1342 )
1343 });
1344
1345 if should_autoclose {
1346 Some(pair.clone())
1347 } else {
1348 None
1349 }
1350 })
1351 });
1352
1353 if let Some(pair) = autoclose_pair {
1354 let selection_ranges = selections
1355 .iter()
1356 .map(|selection| {
1357 let start = selection.start.to_offset(&snapshot);
1358 start..start
1359 })
1360 .collect::<SmallVec<[_; 32]>>();
1361
1362 buffer.edit(selection_ranges, &pair.end, cx);
1363 snapshot = buffer.snapshot(cx);
1364
1365 new_selections = Some(
1366 self.resolve_selections::<usize, _>(left_biased_selections.iter(), &snapshot)
1367 .collect::<Vec<_>>(),
1368 );
1369
1370 if pair.end.len() == 1 {
1371 let mut delta = 0;
1372 bracket_pair_state = Some(BracketPairState {
1373 ranges: selections
1374 .iter()
1375 .map(move |selection| {
1376 let offset = selection.start + delta;
1377 delta += 1;
1378 snapshot.anchor_before(offset)..snapshot.anchor_after(offset)
1379 })
1380 .collect(),
1381 pair,
1382 });
1383 }
1384 }
1385 });
1386
1387 if let Some(new_selections) = new_selections {
1388 self.update_selections(new_selections, None, cx);
1389 }
1390 if let Some(bracket_pair_state) = bracket_pair_state {
1391 self.autoclose_stack.push(bracket_pair_state);
1392 }
1393 }
1394
1395 fn skip_autoclose_end(&mut self, text: &str, cx: &mut ViewContext<Self>) -> bool {
1396 let old_selections = self.local_selections::<usize>(cx);
1397 let autoclose_pair = if let Some(autoclose_pair) = self.autoclose_stack.last() {
1398 autoclose_pair
1399 } else {
1400 return false;
1401 };
1402 if text != autoclose_pair.pair.end {
1403 return false;
1404 }
1405
1406 debug_assert_eq!(old_selections.len(), autoclose_pair.ranges.len());
1407
1408 let buffer = self.buffer.read(cx).snapshot(cx);
1409 if old_selections
1410 .iter()
1411 .zip(autoclose_pair.ranges.iter().map(|r| r.to_offset(&buffer)))
1412 .all(|(selection, autoclose_range)| {
1413 let autoclose_range_end = autoclose_range.end.to_offset(&buffer);
1414 selection.is_empty() && selection.start == autoclose_range_end
1415 })
1416 {
1417 let new_selections = old_selections
1418 .into_iter()
1419 .map(|selection| {
1420 let cursor = selection.start + 1;
1421 Selection {
1422 id: selection.id,
1423 start: cursor,
1424 end: cursor,
1425 reversed: false,
1426 goal: SelectionGoal::None,
1427 }
1428 })
1429 .collect();
1430 self.autoclose_stack.pop();
1431 self.update_selections(new_selections, Some(Autoscroll::Fit), cx);
1432 true
1433 } else {
1434 false
1435 }
1436 }
1437
1438 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
1439 self.start_transaction(cx);
1440 self.select_all(&SelectAll, cx);
1441 self.insert("", cx);
1442 self.end_transaction(cx);
1443 }
1444
1445 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
1446 self.start_transaction(cx);
1447 let mut selections = self.local_selections::<Point>(cx);
1448 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1449 for selection in &mut selections {
1450 if selection.is_empty() {
1451 let head = selection.head().to_display_point(&display_map);
1452 let cursor = movement::left(&display_map, head)
1453 .unwrap()
1454 .to_point(&display_map);
1455 selection.set_head(cursor);
1456 selection.goal = SelectionGoal::None;
1457 }
1458 }
1459 self.update_selections(selections, Some(Autoscroll::Fit), cx);
1460 self.insert("", cx);
1461 self.end_transaction(cx);
1462 }
1463
1464 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
1465 self.start_transaction(cx);
1466 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1467 let mut selections = self.local_selections::<Point>(cx);
1468 for selection in &mut selections {
1469 if selection.is_empty() {
1470 let head = selection.head().to_display_point(&display_map);
1471 let cursor = movement::right(&display_map, head)
1472 .unwrap()
1473 .to_point(&display_map);
1474 selection.set_head(cursor);
1475 selection.goal = SelectionGoal::None;
1476 }
1477 }
1478 self.update_selections(selections, Some(Autoscroll::Fit), cx);
1479 self.insert(&"", cx);
1480 self.end_transaction(cx);
1481 }
1482
1483 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
1484 self.start_transaction(cx);
1485 let tab_size = (self.build_settings)(cx).tab_size;
1486 let mut selections = self.local_selections::<Point>(cx);
1487 let mut last_indent = None;
1488 self.buffer.update(cx, |buffer, cx| {
1489 for selection in &mut selections {
1490 if selection.is_empty() {
1491 let char_column = buffer
1492 .read(cx)
1493 .text_for_range(Point::new(selection.start.row, 0)..selection.start)
1494 .flat_map(str::chars)
1495 .count();
1496 let chars_to_next_tab_stop = tab_size - (char_column % tab_size);
1497 buffer.edit(
1498 [selection.start..selection.start],
1499 " ".repeat(chars_to_next_tab_stop),
1500 cx,
1501 );
1502 selection.start.column += chars_to_next_tab_stop as u32;
1503 selection.end = selection.start;
1504 } else {
1505 let mut start_row = selection.start.row;
1506 let mut end_row = selection.end.row + 1;
1507
1508 // If a selection ends at the beginning of a line, don't indent
1509 // that last line.
1510 if selection.end.column == 0 {
1511 end_row -= 1;
1512 }
1513
1514 // Avoid re-indenting a row that has already been indented by a
1515 // previous selection, but still update this selection's column
1516 // to reflect that indentation.
1517 if let Some((last_indent_row, last_indent_len)) = last_indent {
1518 if last_indent_row == selection.start.row {
1519 selection.start.column += last_indent_len;
1520 start_row += 1;
1521 }
1522 if last_indent_row == selection.end.row {
1523 selection.end.column += last_indent_len;
1524 }
1525 }
1526
1527 for row in start_row..end_row {
1528 let indent_column = buffer.read(cx).indent_column_for_line(row) as usize;
1529 let columns_to_next_tab_stop = tab_size - (indent_column % tab_size);
1530 let row_start = Point::new(row, 0);
1531 buffer.edit(
1532 [row_start..row_start],
1533 " ".repeat(columns_to_next_tab_stop),
1534 cx,
1535 );
1536
1537 // Update this selection's endpoints to reflect the indentation.
1538 if row == selection.start.row {
1539 selection.start.column += columns_to_next_tab_stop as u32;
1540 }
1541 if row == selection.end.row {
1542 selection.end.column += columns_to_next_tab_stop as u32;
1543 }
1544
1545 last_indent = Some((row, columns_to_next_tab_stop as u32));
1546 }
1547 }
1548 }
1549 });
1550
1551 self.update_selections(selections, Some(Autoscroll::Fit), cx);
1552 self.end_transaction(cx);
1553 }
1554
1555 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
1556 self.start_transaction(cx);
1557 let tab_size = (self.build_settings)(cx).tab_size;
1558 let selections = self.local_selections::<Point>(cx);
1559 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1560 let mut deletion_ranges = Vec::new();
1561 let mut last_outdent = None;
1562 {
1563 let buffer = self.buffer.read(cx).read(cx);
1564 for selection in &selections {
1565 let mut rows = selection.spanned_rows(false, &display_map);
1566
1567 // Avoid re-outdenting a row that has already been outdented by a
1568 // previous selection.
1569 if let Some(last_row) = last_outdent {
1570 if last_row == rows.start {
1571 rows.start += 1;
1572 }
1573 }
1574
1575 for row in rows {
1576 let column = buffer.indent_column_for_line(row) as usize;
1577 if column > 0 {
1578 let mut deletion_len = (column % tab_size) as u32;
1579 if deletion_len == 0 {
1580 deletion_len = tab_size as u32;
1581 }
1582 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
1583 last_outdent = Some(row);
1584 }
1585 }
1586 }
1587 }
1588 self.buffer.update(cx, |buffer, cx| {
1589 buffer.edit(deletion_ranges, "", cx);
1590 });
1591
1592 self.update_selections(
1593 self.local_selections::<usize>(cx),
1594 Some(Autoscroll::Fit),
1595 cx,
1596 );
1597 self.end_transaction(cx);
1598 }
1599
1600 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
1601 self.start_transaction(cx);
1602
1603 let selections = self.local_selections::<Point>(cx);
1604 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1605 let buffer = self.buffer.read(cx).snapshot(cx);
1606
1607 let mut new_cursors = Vec::new();
1608 let mut edit_ranges = Vec::new();
1609 let mut selections = selections.iter().peekable();
1610 while let Some(selection) = selections.next() {
1611 let mut rows = selection.spanned_rows(false, &display_map);
1612 let goal_display_column = selection.head().to_display_point(&display_map).column();
1613
1614 // Accumulate contiguous regions of rows that we want to delete.
1615 while let Some(next_selection) = selections.peek() {
1616 let next_rows = next_selection.spanned_rows(false, &display_map);
1617 if next_rows.start <= rows.end {
1618 rows.end = next_rows.end;
1619 selections.next().unwrap();
1620 } else {
1621 break;
1622 }
1623 }
1624
1625 let mut edit_start = Point::new(rows.start, 0).to_offset(&buffer);
1626 let edit_end;
1627 let cursor_buffer_row;
1628 if buffer.max_point().row >= rows.end {
1629 // If there's a line after the range, delete the \n from the end of the row range
1630 // and position the cursor on the next line.
1631 edit_end = Point::new(rows.end, 0).to_offset(&buffer);
1632 cursor_buffer_row = rows.end;
1633 } else {
1634 // If there isn't a line after the range, delete the \n from the line before the
1635 // start of the row range and position the cursor there.
1636 edit_start = edit_start.saturating_sub(1);
1637 edit_end = buffer.len();
1638 cursor_buffer_row = rows.start.saturating_sub(1);
1639 }
1640
1641 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
1642 *cursor.column_mut() =
1643 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
1644
1645 new_cursors.push((
1646 selection.id,
1647 buffer.anchor_after(cursor.to_point(&display_map)),
1648 ));
1649 edit_ranges.push(edit_start..edit_end);
1650 }
1651
1652 let buffer = self.buffer.update(cx, |buffer, cx| {
1653 buffer.edit(edit_ranges, "", cx);
1654 buffer.snapshot(cx)
1655 });
1656 let new_selections = new_cursors
1657 .into_iter()
1658 .map(|(id, cursor)| {
1659 let cursor = cursor.to_point(&buffer);
1660 Selection {
1661 id,
1662 start: cursor,
1663 end: cursor,
1664 reversed: false,
1665 goal: SelectionGoal::None,
1666 }
1667 })
1668 .collect();
1669 self.update_selections(new_selections, Some(Autoscroll::Fit), cx);
1670 self.end_transaction(cx);
1671 }
1672
1673 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
1674 self.start_transaction(cx);
1675
1676 let selections = self.local_selections::<Point>(cx);
1677 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1678 let buffer = &display_map.buffer_snapshot;
1679
1680 let mut edits = Vec::new();
1681 let mut selections_iter = selections.iter().peekable();
1682 while let Some(selection) = selections_iter.next() {
1683 // Avoid duplicating the same lines twice.
1684 let mut rows = selection.spanned_rows(false, &display_map);
1685
1686 while let Some(next_selection) = selections_iter.peek() {
1687 let next_rows = next_selection.spanned_rows(false, &display_map);
1688 if next_rows.start <= rows.end - 1 {
1689 rows.end = next_rows.end;
1690 selections_iter.next().unwrap();
1691 } else {
1692 break;
1693 }
1694 }
1695
1696 // Copy the text from the selected row region and splice it at the start of the region.
1697 let start = Point::new(rows.start, 0);
1698 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
1699 let text = buffer
1700 .text_for_range(start..end)
1701 .chain(Some("\n"))
1702 .collect::<String>();
1703 edits.push((start, text, rows.len() as u32));
1704 }
1705
1706 self.buffer.update(cx, |buffer, cx| {
1707 for (point, text, _) in edits.into_iter().rev() {
1708 buffer.edit(Some(point..point), text, cx);
1709 }
1710 });
1711
1712 self.request_autoscroll(Autoscroll::Fit, cx);
1713 self.end_transaction(cx);
1714 }
1715
1716 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
1717 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1718 let buffer = self.buffer.read(cx).snapshot(cx);
1719
1720 let mut edits = Vec::new();
1721 let mut unfold_ranges = Vec::new();
1722 let mut refold_ranges = Vec::new();
1723
1724 let selections = self.local_selections::<Point>(cx);
1725 let mut selections = selections.iter().peekable();
1726 let mut contiguous_row_selections = Vec::new();
1727 let mut new_selections = Vec::new();
1728
1729 while let Some(selection) = selections.next() {
1730 // Find all the selections that span a contiguous row range
1731 contiguous_row_selections.push(selection.clone());
1732 let start_row = selection.start.row;
1733 let mut end_row = if selection.end.column > 0 || selection.is_empty() {
1734 display_map.next_line_boundary(selection.end).0.row + 1
1735 } else {
1736 selection.end.row
1737 };
1738
1739 while let Some(next_selection) = selections.peek() {
1740 if next_selection.start.row <= end_row {
1741 end_row = if next_selection.end.column > 0 || next_selection.is_empty() {
1742 display_map.next_line_boundary(next_selection.end).0.row + 1
1743 } else {
1744 next_selection.end.row
1745 };
1746 contiguous_row_selections.push(selections.next().unwrap().clone());
1747 } else {
1748 break;
1749 }
1750 }
1751
1752 // Move the text spanned by the row range to be before the line preceding the row range
1753 if start_row > 0 {
1754 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
1755 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
1756 let insertion_point = display_map
1757 .prev_line_boundary(Point::new(start_row - 1, 0))
1758 .0;
1759
1760 // Don't move lines across excerpts
1761 if !buffer.range_contains_excerpt_boundary(insertion_point..range_to_move.end) {
1762 let text = buffer
1763 .text_for_range(range_to_move.clone())
1764 .flat_map(|s| s.chars())
1765 .skip(1)
1766 .chain(['\n'])
1767 .collect::<String>();
1768
1769 edits.push((
1770 buffer.anchor_after(range_to_move.start)
1771 ..buffer.anchor_before(range_to_move.end),
1772 String::new(),
1773 ));
1774 let insertion_anchor = buffer.anchor_after(insertion_point);
1775 edits.push((insertion_anchor.clone()..insertion_anchor, text));
1776
1777 let row_delta = range_to_move.start.row - insertion_point.row + 1;
1778
1779 // Move selections up
1780 new_selections.extend(contiguous_row_selections.drain(..).map(
1781 |mut selection| {
1782 selection.start.row -= row_delta;
1783 selection.end.row -= row_delta;
1784 selection
1785 },
1786 ));
1787
1788 // Move folds up
1789 unfold_ranges.push(range_to_move.clone());
1790 for fold in display_map.folds_in_range(
1791 buffer.anchor_before(range_to_move.start)
1792 ..buffer.anchor_after(range_to_move.end),
1793 ) {
1794 let mut start = fold.start.to_point(&buffer);
1795 let mut end = fold.end.to_point(&buffer);
1796 start.row -= row_delta;
1797 end.row -= row_delta;
1798 refold_ranges.push(start..end);
1799 }
1800 }
1801 }
1802
1803 // If we didn't move line(s), preserve the existing selections
1804 new_selections.extend(contiguous_row_selections.drain(..));
1805 }
1806
1807 self.start_transaction(cx);
1808 self.unfold_ranges(unfold_ranges, cx);
1809 self.buffer.update(cx, |buffer, cx| {
1810 for (range, text) in edits {
1811 buffer.edit([range], text, cx);
1812 }
1813 });
1814 self.fold_ranges(refold_ranges, cx);
1815 self.update_selections(new_selections, Some(Autoscroll::Fit), cx);
1816 self.end_transaction(cx);
1817 }
1818
1819 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
1820 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1821 let buffer = self.buffer.read(cx).snapshot(cx);
1822
1823 let mut edits = Vec::new();
1824 let mut unfold_ranges = Vec::new();
1825 let mut refold_ranges = Vec::new();
1826
1827 let selections = self.local_selections::<Point>(cx);
1828 let mut selections = selections.iter().peekable();
1829 let mut contiguous_row_selections = Vec::new();
1830 let mut new_selections = Vec::new();
1831
1832 while let Some(selection) = selections.next() {
1833 // Find all the selections that span a contiguous row range
1834 contiguous_row_selections.push(selection.clone());
1835 let start_row = selection.start.row;
1836 let mut end_row = if selection.end.column > 0 || selection.is_empty() {
1837 display_map.next_line_boundary(selection.end).0.row + 1
1838 } else {
1839 selection.end.row
1840 };
1841
1842 while let Some(next_selection) = selections.peek() {
1843 if next_selection.start.row <= end_row {
1844 end_row = if next_selection.end.column > 0 || next_selection.is_empty() {
1845 display_map.next_line_boundary(next_selection.end).0.row + 1
1846 } else {
1847 next_selection.end.row
1848 };
1849 contiguous_row_selections.push(selections.next().unwrap().clone());
1850 } else {
1851 break;
1852 }
1853 }
1854
1855 // Move the text spanned by the row range to be after the last line of the row range
1856 if end_row <= buffer.max_point().row {
1857 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
1858 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
1859
1860 // Don't move lines across excerpt boundaries
1861 if !buffer.range_contains_excerpt_boundary(range_to_move.start..insertion_point) {
1862 let mut text = String::from("\n");
1863 text.extend(buffer.text_for_range(range_to_move.clone()));
1864 text.pop(); // Drop trailing newline
1865 edits.push((
1866 buffer.anchor_after(range_to_move.start)
1867 ..buffer.anchor_before(range_to_move.end),
1868 String::new(),
1869 ));
1870 let insertion_anchor = buffer.anchor_after(insertion_point);
1871 edits.push((insertion_anchor.clone()..insertion_anchor, text));
1872
1873 let row_delta = insertion_point.row - range_to_move.end.row + 1;
1874
1875 // Move selections down
1876 new_selections.extend(contiguous_row_selections.drain(..).map(
1877 |mut selection| {
1878 selection.start.row += row_delta;
1879 selection.end.row += row_delta;
1880 selection
1881 },
1882 ));
1883
1884 // Move folds down
1885 unfold_ranges.push(range_to_move.clone());
1886 for fold in display_map.folds_in_range(
1887 buffer.anchor_before(range_to_move.start)
1888 ..buffer.anchor_after(range_to_move.end),
1889 ) {
1890 let mut start = fold.start.to_point(&buffer);
1891 let mut end = fold.end.to_point(&buffer);
1892 start.row += row_delta;
1893 end.row += row_delta;
1894 refold_ranges.push(start..end);
1895 }
1896 }
1897 }
1898
1899 // If we didn't move line(s), preserve the existing selections
1900 new_selections.extend(contiguous_row_selections.drain(..));
1901 }
1902
1903 self.start_transaction(cx);
1904 self.unfold_ranges(unfold_ranges, cx);
1905 self.buffer.update(cx, |buffer, cx| {
1906 for (range, text) in edits {
1907 buffer.edit([range], text, cx);
1908 }
1909 });
1910 self.fold_ranges(refold_ranges, cx);
1911 self.update_selections(new_selections, Some(Autoscroll::Fit), cx);
1912 self.end_transaction(cx);
1913 }
1914
1915 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
1916 self.start_transaction(cx);
1917 let mut text = String::new();
1918 let mut selections = self.local_selections::<Point>(cx);
1919 let mut clipboard_selections = Vec::with_capacity(selections.len());
1920 {
1921 let buffer = self.buffer.read(cx).read(cx);
1922 let max_point = buffer.max_point();
1923 for selection in &mut selections {
1924 let is_entire_line = selection.is_empty();
1925 if is_entire_line {
1926 selection.start = Point::new(selection.start.row, 0);
1927 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
1928 }
1929 let mut len = 0;
1930 for chunk in buffer.text_for_range(selection.start..selection.end) {
1931 text.push_str(chunk);
1932 len += chunk.len();
1933 }
1934 clipboard_selections.push(ClipboardSelection {
1935 len,
1936 is_entire_line,
1937 });
1938 }
1939 }
1940 self.update_selections(selections, Some(Autoscroll::Fit), cx);
1941 self.insert("", cx);
1942 self.end_transaction(cx);
1943
1944 cx.as_mut()
1945 .write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
1946 }
1947
1948 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
1949 let selections = self.local_selections::<Point>(cx);
1950 let mut text = String::new();
1951 let mut clipboard_selections = Vec::with_capacity(selections.len());
1952 {
1953 let buffer = self.buffer.read(cx).read(cx);
1954 let max_point = buffer.max_point();
1955 for selection in selections.iter() {
1956 let mut start = selection.start;
1957 let mut end = selection.end;
1958 let is_entire_line = selection.is_empty();
1959 if is_entire_line {
1960 start = Point::new(start.row, 0);
1961 end = cmp::min(max_point, Point::new(start.row + 1, 0));
1962 }
1963 let mut len = 0;
1964 for chunk in buffer.text_for_range(start..end) {
1965 text.push_str(chunk);
1966 len += chunk.len();
1967 }
1968 clipboard_selections.push(ClipboardSelection {
1969 len,
1970 is_entire_line,
1971 });
1972 }
1973 }
1974
1975 cx.as_mut()
1976 .write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
1977 }
1978
1979 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
1980 if let Some(item) = cx.as_mut().read_from_clipboard() {
1981 let clipboard_text = item.text();
1982 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
1983 let mut selections = self.local_selections::<usize>(cx);
1984 let all_selections_were_entire_line =
1985 clipboard_selections.iter().all(|s| s.is_entire_line);
1986 if clipboard_selections.len() != selections.len() {
1987 clipboard_selections.clear();
1988 }
1989
1990 let mut delta = 0_isize;
1991 let mut start_offset = 0;
1992 for (i, selection) in selections.iter_mut().enumerate() {
1993 let to_insert;
1994 let entire_line;
1995 if let Some(clipboard_selection) = clipboard_selections.get(i) {
1996 let end_offset = start_offset + clipboard_selection.len;
1997 to_insert = &clipboard_text[start_offset..end_offset];
1998 entire_line = clipboard_selection.is_entire_line;
1999 start_offset = end_offset
2000 } else {
2001 to_insert = clipboard_text.as_str();
2002 entire_line = all_selections_were_entire_line;
2003 }
2004
2005 selection.start = (selection.start as isize + delta) as usize;
2006 selection.end = (selection.end as isize + delta) as usize;
2007
2008 self.buffer.update(cx, |buffer, cx| {
2009 // If the corresponding selection was empty when this slice of the
2010 // clipboard text was written, then the entire line containing the
2011 // selection was copied. If this selection is also currently empty,
2012 // then paste the line before the current line of the buffer.
2013 let range = if selection.is_empty() && entire_line {
2014 let column = selection.start.to_point(&buffer.read(cx)).column as usize;
2015 let line_start = selection.start - column;
2016 line_start..line_start
2017 } else {
2018 selection.start..selection.end
2019 };
2020
2021 delta += to_insert.len() as isize - range.len() as isize;
2022 buffer.edit([range], to_insert, cx);
2023 selection.start += to_insert.len();
2024 selection.end = selection.start;
2025 });
2026 }
2027 self.update_selections(selections, Some(Autoscroll::Fit), cx);
2028 } else {
2029 self.insert(clipboard_text, cx);
2030 }
2031 }
2032 }
2033
2034 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
2035 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
2036 if let Some((selections, _)) = self.selection_history.get(&tx_id).cloned() {
2037 self.set_selections(selections, cx);
2038 }
2039 self.request_autoscroll(Autoscroll::Fit, cx);
2040 }
2041 }
2042
2043 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
2044 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
2045 if let Some((_, Some(selections))) = self.selection_history.get(&tx_id).cloned() {
2046 self.set_selections(selections, cx);
2047 }
2048 self.request_autoscroll(Autoscroll::Fit, cx);
2049 }
2050 }
2051
2052 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
2053 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2054 let mut selections = self.local_selections::<Point>(cx);
2055 for selection in &mut selections {
2056 let start = selection.start.to_display_point(&display_map);
2057 let end = selection.end.to_display_point(&display_map);
2058
2059 if start != end {
2060 selection.end = selection.start.clone();
2061 } else {
2062 let cursor = movement::left(&display_map, start)
2063 .unwrap()
2064 .to_point(&display_map);
2065 selection.start = cursor.clone();
2066 selection.end = cursor;
2067 }
2068 selection.reversed = false;
2069 selection.goal = SelectionGoal::None;
2070 }
2071 self.update_selections(selections, Some(Autoscroll::Fit), cx);
2072 }
2073
2074 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
2075 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2076 let mut selections = self.local_selections::<Point>(cx);
2077 for selection in &mut selections {
2078 let head = selection.head().to_display_point(&display_map);
2079 let cursor = movement::left(&display_map, head)
2080 .unwrap()
2081 .to_point(&display_map);
2082 selection.set_head(cursor);
2083 selection.goal = SelectionGoal::None;
2084 }
2085 self.update_selections(selections, Some(Autoscroll::Fit), cx);
2086 }
2087
2088 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
2089 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2090 let mut selections = self.local_selections::<Point>(cx);
2091 for selection in &mut selections {
2092 let start = selection.start.to_display_point(&display_map);
2093 let end = selection.end.to_display_point(&display_map);
2094
2095 if start != end {
2096 selection.start = selection.end.clone();
2097 } else {
2098 let cursor = movement::right(&display_map, end)
2099 .unwrap()
2100 .to_point(&display_map);
2101 selection.start = cursor;
2102 selection.end = cursor;
2103 }
2104 selection.reversed = false;
2105 selection.goal = SelectionGoal::None;
2106 }
2107 self.update_selections(selections, Some(Autoscroll::Fit), cx);
2108 }
2109
2110 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
2111 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2112 let mut selections = self.local_selections::<Point>(cx);
2113 for selection in &mut selections {
2114 let head = selection.head().to_display_point(&display_map);
2115 let cursor = movement::right(&display_map, head)
2116 .unwrap()
2117 .to_point(&display_map);
2118 selection.set_head(cursor);
2119 selection.goal = SelectionGoal::None;
2120 }
2121 self.update_selections(selections, Some(Autoscroll::Fit), cx);
2122 }
2123
2124 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
2125 if matches!(self.mode, EditorMode::SingleLine) {
2126 cx.propagate_action();
2127 return;
2128 }
2129
2130 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2131 let mut selections = self.local_selections::<Point>(cx);
2132 for selection in &mut selections {
2133 let start = selection.start.to_display_point(&display_map);
2134 let end = selection.end.to_display_point(&display_map);
2135 if start != end {
2136 selection.goal = SelectionGoal::None;
2137 }
2138
2139 let (start, goal) = movement::up(&display_map, start, selection.goal).unwrap();
2140 let cursor = start.to_point(&display_map);
2141 selection.start = cursor;
2142 selection.end = cursor;
2143 selection.goal = goal;
2144 selection.reversed = false;
2145 }
2146 self.update_selections(selections, Some(Autoscroll::Fit), cx);
2147 }
2148
2149 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
2150 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2151 let mut selections = self.local_selections::<Point>(cx);
2152 for selection in &mut selections {
2153 let head = selection.head().to_display_point(&display_map);
2154 let (head, goal) = movement::up(&display_map, head, selection.goal).unwrap();
2155 let cursor = head.to_point(&display_map);
2156 selection.set_head(cursor);
2157 selection.goal = goal;
2158 }
2159 self.update_selections(selections, Some(Autoscroll::Fit), cx);
2160 }
2161
2162 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
2163 if matches!(self.mode, EditorMode::SingleLine) {
2164 cx.propagate_action();
2165 return;
2166 }
2167
2168 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2169 let mut selections = self.local_selections::<Point>(cx);
2170 for selection in &mut selections {
2171 let start = selection.start.to_display_point(&display_map);
2172 let end = selection.end.to_display_point(&display_map);
2173 if start != end {
2174 selection.goal = SelectionGoal::None;
2175 }
2176
2177 let (start, goal) = movement::down(&display_map, end, selection.goal).unwrap();
2178 let cursor = start.to_point(&display_map);
2179 selection.start = cursor;
2180 selection.end = cursor;
2181 selection.goal = goal;
2182 selection.reversed = false;
2183 }
2184 self.update_selections(selections, Some(Autoscroll::Fit), cx);
2185 }
2186
2187 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
2188 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2189 let mut selections = self.local_selections::<Point>(cx);
2190 for selection in &mut selections {
2191 let head = selection.head().to_display_point(&display_map);
2192 let (head, goal) = movement::down(&display_map, head, selection.goal).unwrap();
2193 let cursor = head.to_point(&display_map);
2194 selection.set_head(cursor);
2195 selection.goal = goal;
2196 }
2197 self.update_selections(selections, Some(Autoscroll::Fit), cx);
2198 }
2199
2200 pub fn move_to_previous_word_boundary(
2201 &mut self,
2202 _: &MoveToPreviousWordBoundary,
2203 cx: &mut ViewContext<Self>,
2204 ) {
2205 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2206 let mut selections = self.local_selections::<Point>(cx);
2207 for selection in &mut selections {
2208 let head = selection.head().to_display_point(&display_map);
2209 let cursor = movement::prev_word_boundary(&display_map, head).to_point(&display_map);
2210 selection.start = cursor.clone();
2211 selection.end = cursor;
2212 selection.reversed = false;
2213 selection.goal = SelectionGoal::None;
2214 }
2215 self.update_selections(selections, Some(Autoscroll::Fit), cx);
2216 }
2217
2218 pub fn select_to_previous_word_boundary(
2219 &mut self,
2220 _: &SelectToPreviousWordBoundary,
2221 cx: &mut ViewContext<Self>,
2222 ) {
2223 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2224 let mut selections = self.local_selections::<Point>(cx);
2225 for selection in &mut selections {
2226 let head = selection.head().to_display_point(&display_map);
2227 let cursor = movement::prev_word_boundary(&display_map, head).to_point(&display_map);
2228 selection.set_head(cursor);
2229 selection.goal = SelectionGoal::None;
2230 }
2231 self.update_selections(selections, Some(Autoscroll::Fit), cx);
2232 }
2233
2234 pub fn delete_to_previous_word_boundary(
2235 &mut self,
2236 _: &DeleteToPreviousWordBoundary,
2237 cx: &mut ViewContext<Self>,
2238 ) {
2239 self.start_transaction(cx);
2240 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2241 let mut selections = self.local_selections::<Point>(cx);
2242 for selection in &mut selections {
2243 if selection.is_empty() {
2244 let head = selection.head().to_display_point(&display_map);
2245 let cursor =
2246 movement::prev_word_boundary(&display_map, head).to_point(&display_map);
2247 selection.set_head(cursor);
2248 selection.goal = SelectionGoal::None;
2249 }
2250 }
2251 self.update_selections(selections, Some(Autoscroll::Fit), cx);
2252 self.insert("", cx);
2253 self.end_transaction(cx);
2254 }
2255
2256 pub fn move_to_next_word_boundary(
2257 &mut self,
2258 _: &MoveToNextWordBoundary,
2259 cx: &mut ViewContext<Self>,
2260 ) {
2261 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2262 let mut selections = self.local_selections::<Point>(cx);
2263 for selection in &mut selections {
2264 let head = selection.head().to_display_point(&display_map);
2265 let cursor = movement::next_word_boundary(&display_map, head).to_point(&display_map);
2266 selection.start = cursor;
2267 selection.end = cursor;
2268 selection.reversed = false;
2269 selection.goal = SelectionGoal::None;
2270 }
2271 self.update_selections(selections, Some(Autoscroll::Fit), cx);
2272 }
2273
2274 pub fn select_to_next_word_boundary(
2275 &mut self,
2276 _: &SelectToNextWordBoundary,
2277 cx: &mut ViewContext<Self>,
2278 ) {
2279 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2280 let mut selections = self.local_selections::<Point>(cx);
2281 for selection in &mut selections {
2282 let head = selection.head().to_display_point(&display_map);
2283 let cursor = movement::next_word_boundary(&display_map, head).to_point(&display_map);
2284 selection.set_head(cursor);
2285 selection.goal = SelectionGoal::None;
2286 }
2287 self.update_selections(selections, Some(Autoscroll::Fit), cx);
2288 }
2289
2290 pub fn delete_to_next_word_boundary(
2291 &mut self,
2292 _: &DeleteToNextWordBoundary,
2293 cx: &mut ViewContext<Self>,
2294 ) {
2295 self.start_transaction(cx);
2296 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2297 let mut selections = self.local_selections::<Point>(cx);
2298 for selection in &mut selections {
2299 if selection.is_empty() {
2300 let head = selection.head().to_display_point(&display_map);
2301 let cursor =
2302 movement::next_word_boundary(&display_map, head).to_point(&display_map);
2303 selection.set_head(cursor);
2304 selection.goal = SelectionGoal::None;
2305 }
2306 }
2307 self.update_selections(selections, Some(Autoscroll::Fit), cx);
2308 self.insert("", cx);
2309 self.end_transaction(cx);
2310 }
2311
2312 pub fn move_to_beginning_of_line(
2313 &mut self,
2314 _: &MoveToBeginningOfLine,
2315 cx: &mut ViewContext<Self>,
2316 ) {
2317 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2318 let mut selections = self.local_selections::<Point>(cx);
2319 for selection in &mut selections {
2320 let head = selection.head().to_display_point(&display_map);
2321 let new_head = movement::line_beginning(&display_map, head, true);
2322 let cursor = new_head.to_point(&display_map);
2323 selection.start = cursor;
2324 selection.end = cursor;
2325 selection.reversed = false;
2326 selection.goal = SelectionGoal::None;
2327 }
2328 self.update_selections(selections, Some(Autoscroll::Fit), cx);
2329 }
2330
2331 pub fn select_to_beginning_of_line(
2332 &mut self,
2333 SelectToBeginningOfLine(toggle_indent): &SelectToBeginningOfLine,
2334 cx: &mut ViewContext<Self>,
2335 ) {
2336 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2337 let mut selections = self.local_selections::<Point>(cx);
2338 for selection in &mut selections {
2339 let head = selection.head().to_display_point(&display_map);
2340 let new_head = movement::line_beginning(&display_map, head, *toggle_indent);
2341 selection.set_head(new_head.to_point(&display_map));
2342 selection.goal = SelectionGoal::None;
2343 }
2344 self.update_selections(selections, Some(Autoscroll::Fit), cx);
2345 }
2346
2347 pub fn delete_to_beginning_of_line(
2348 &mut self,
2349 _: &DeleteToBeginningOfLine,
2350 cx: &mut ViewContext<Self>,
2351 ) {
2352 self.start_transaction(cx);
2353 self.select_to_beginning_of_line(&SelectToBeginningOfLine(false), cx);
2354 self.backspace(&Backspace, cx);
2355 self.end_transaction(cx);
2356 }
2357
2358 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
2359 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2360 let mut selections = self.local_selections::<Point>(cx);
2361 {
2362 for selection in &mut selections {
2363 let head = selection.head().to_display_point(&display_map);
2364 let new_head = movement::line_end(&display_map, head);
2365 let anchor = new_head.to_point(&display_map);
2366 selection.start = anchor.clone();
2367 selection.end = anchor;
2368 selection.reversed = false;
2369 selection.goal = SelectionGoal::None;
2370 }
2371 }
2372 self.update_selections(selections, Some(Autoscroll::Fit), cx);
2373 }
2374
2375 pub fn select_to_end_of_line(&mut self, _: &SelectToEndOfLine, cx: &mut ViewContext<Self>) {
2376 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2377 let mut selections = self.local_selections::<Point>(cx);
2378 for selection in &mut selections {
2379 let head = selection.head().to_display_point(&display_map);
2380 let new_head = movement::line_end(&display_map, head);
2381 selection.set_head(new_head.to_point(&display_map));
2382 selection.goal = SelectionGoal::None;
2383 }
2384 self.update_selections(selections, Some(Autoscroll::Fit), cx);
2385 }
2386
2387 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
2388 self.start_transaction(cx);
2389 self.select_to_end_of_line(&SelectToEndOfLine, cx);
2390 self.delete(&Delete, cx);
2391 self.end_transaction(cx);
2392 }
2393
2394 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
2395 self.start_transaction(cx);
2396 self.select_to_end_of_line(&SelectToEndOfLine, cx);
2397 self.cut(&Cut, cx);
2398 self.end_transaction(cx);
2399 }
2400
2401 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
2402 if matches!(self.mode, EditorMode::SingleLine) {
2403 cx.propagate_action();
2404 return;
2405 }
2406
2407 self.push_to_navigation_history(cx);
2408
2409 let selection = Selection {
2410 id: post_inc(&mut self.next_selection_id),
2411 start: 0,
2412 end: 0,
2413 reversed: false,
2414 goal: SelectionGoal::None,
2415 };
2416 self.update_selections(vec![selection], Some(Autoscroll::Fit), cx);
2417 }
2418
2419 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
2420 let mut selection = self.local_selections::<Point>(cx).last().unwrap().clone();
2421 selection.set_head(Point::zero());
2422 self.update_selections(vec![selection], Some(Autoscroll::Fit), cx);
2423 }
2424
2425 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
2426 if matches!(self.mode, EditorMode::SingleLine) {
2427 cx.propagate_action();
2428 return;
2429 }
2430
2431 self.push_to_navigation_history(cx);
2432
2433 let cursor = self.buffer.read(cx).read(cx).len();
2434 let selection = Selection {
2435 id: post_inc(&mut self.next_selection_id),
2436 start: cursor,
2437 end: cursor,
2438 reversed: false,
2439 goal: SelectionGoal::None,
2440 };
2441 self.update_selections(vec![selection], Some(Autoscroll::Fit), cx);
2442 }
2443
2444 fn push_to_navigation_history(&self, cx: &mut ViewContext<Self>) {
2445 if let Some(navigation) = &self.navigation {
2446 let buffer = self.buffer.read(cx).read(cx);
2447 if let Some(last_selection) = self.selections.iter().max_by_key(|s| s.id) {
2448 let anchor = last_selection.head();
2449 let offset = anchor.to_offset(&buffer);
2450 drop(buffer);
2451 navigation.push(Some(NavigationData { anchor, offset }), cx);
2452 }
2453 }
2454 }
2455
2456 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
2457 let mut selection = self.local_selections::<usize>(cx).first().unwrap().clone();
2458 selection.set_head(self.buffer.read(cx).read(cx).len());
2459 self.update_selections(vec![selection], Some(Autoscroll::Fit), cx);
2460 }
2461
2462 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
2463 let selection = Selection {
2464 id: post_inc(&mut self.next_selection_id),
2465 start: 0,
2466 end: self.buffer.read(cx).read(cx).len(),
2467 reversed: false,
2468 goal: SelectionGoal::None,
2469 };
2470 self.update_selections(vec![selection], None, cx);
2471 }
2472
2473 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
2474 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2475 let mut selections = self.local_selections::<Point>(cx);
2476 let max_point = display_map.buffer_snapshot.max_point();
2477 for selection in &mut selections {
2478 let rows = selection.spanned_rows(true, &display_map);
2479 selection.start = Point::new(rows.start, 0);
2480 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
2481 selection.reversed = false;
2482 }
2483 self.update_selections(selections, Some(Autoscroll::Fit), cx);
2484 }
2485
2486 pub fn split_selection_into_lines(
2487 &mut self,
2488 _: &SplitSelectionIntoLines,
2489 cx: &mut ViewContext<Self>,
2490 ) {
2491 let mut to_unfold = Vec::new();
2492 let mut new_selections = Vec::new();
2493 {
2494 let selections = self.local_selections::<Point>(cx);
2495 let buffer = self.buffer.read(cx).read(cx);
2496 for selection in selections {
2497 for row in selection.start.row..selection.end.row {
2498 let cursor = Point::new(row, buffer.line_len(row));
2499 new_selections.push(Selection {
2500 id: post_inc(&mut self.next_selection_id),
2501 start: cursor,
2502 end: cursor,
2503 reversed: false,
2504 goal: SelectionGoal::None,
2505 });
2506 }
2507 new_selections.push(Selection {
2508 id: selection.id,
2509 start: selection.end,
2510 end: selection.end,
2511 reversed: false,
2512 goal: SelectionGoal::None,
2513 });
2514 to_unfold.push(selection.start..selection.end);
2515 }
2516 }
2517 self.unfold_ranges(to_unfold, cx);
2518 self.update_selections(new_selections, Some(Autoscroll::Fit), cx);
2519 }
2520
2521 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
2522 self.add_selection(true, cx);
2523 }
2524
2525 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
2526 self.add_selection(false, cx);
2527 }
2528
2529 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
2530 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2531 let mut selections = self.local_selections::<Point>(cx);
2532 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
2533 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
2534 let range = oldest_selection.display_range(&display_map).sorted();
2535 let columns = cmp::min(range.start.column(), range.end.column())
2536 ..cmp::max(range.start.column(), range.end.column());
2537
2538 selections.clear();
2539 let mut stack = Vec::new();
2540 for row in range.start.row()..=range.end.row() {
2541 if let Some(selection) = self.build_columnar_selection(
2542 &display_map,
2543 row,
2544 &columns,
2545 oldest_selection.reversed,
2546 ) {
2547 stack.push(selection.id);
2548 selections.push(selection);
2549 }
2550 }
2551
2552 if above {
2553 stack.reverse();
2554 }
2555
2556 AddSelectionsState { above, stack }
2557 });
2558
2559 let last_added_selection = *state.stack.last().unwrap();
2560 let mut new_selections = Vec::new();
2561 if above == state.above {
2562 let end_row = if above {
2563 0
2564 } else {
2565 display_map.max_point().row()
2566 };
2567
2568 'outer: for selection in selections {
2569 if selection.id == last_added_selection {
2570 let range = selection.display_range(&display_map).sorted();
2571 debug_assert_eq!(range.start.row(), range.end.row());
2572 let mut row = range.start.row();
2573 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
2574 {
2575 start..end
2576 } else {
2577 cmp::min(range.start.column(), range.end.column())
2578 ..cmp::max(range.start.column(), range.end.column())
2579 };
2580
2581 while row != end_row {
2582 if above {
2583 row -= 1;
2584 } else {
2585 row += 1;
2586 }
2587
2588 if let Some(new_selection) = self.build_columnar_selection(
2589 &display_map,
2590 row,
2591 &columns,
2592 selection.reversed,
2593 ) {
2594 state.stack.push(new_selection.id);
2595 if above {
2596 new_selections.push(new_selection);
2597 new_selections.push(selection);
2598 } else {
2599 new_selections.push(selection);
2600 new_selections.push(new_selection);
2601 }
2602
2603 continue 'outer;
2604 }
2605 }
2606 }
2607
2608 new_selections.push(selection);
2609 }
2610 } else {
2611 new_selections = selections;
2612 new_selections.retain(|s| s.id != last_added_selection);
2613 state.stack.pop();
2614 }
2615
2616 self.update_selections(new_selections, Some(Autoscroll::Fit), cx);
2617 if state.stack.len() > 1 {
2618 self.add_selections_state = Some(state);
2619 }
2620 }
2621
2622 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
2623 let replace_newest = action.0;
2624 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2625 let buffer = &display_map.buffer_snapshot;
2626 let mut selections = self.local_selections::<usize>(cx);
2627 if let Some(mut select_next_state) = self.select_next_state.take() {
2628 let query = &select_next_state.query;
2629 if !select_next_state.done {
2630 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
2631 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
2632 let mut next_selected_range = None;
2633
2634 let bytes_after_last_selection =
2635 buffer.bytes_in_range(last_selection.end..buffer.len());
2636 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
2637 let query_matches = query
2638 .stream_find_iter(bytes_after_last_selection)
2639 .map(|result| (last_selection.end, result))
2640 .chain(
2641 query
2642 .stream_find_iter(bytes_before_first_selection)
2643 .map(|result| (0, result)),
2644 );
2645 for (start_offset, query_match) in query_matches {
2646 let query_match = query_match.unwrap(); // can only fail due to I/O
2647 let offset_range =
2648 start_offset + query_match.start()..start_offset + query_match.end();
2649 let display_range = offset_range.start.to_display_point(&display_map)
2650 ..offset_range.end.to_display_point(&display_map);
2651
2652 if !select_next_state.wordwise
2653 || (!movement::is_inside_word(&display_map, display_range.start)
2654 && !movement::is_inside_word(&display_map, display_range.end))
2655 {
2656 next_selected_range = Some(offset_range);
2657 break;
2658 }
2659 }
2660
2661 if let Some(next_selected_range) = next_selected_range {
2662 if replace_newest {
2663 if let Some(newest_id) =
2664 selections.iter().max_by_key(|s| s.id).map(|s| s.id)
2665 {
2666 selections.retain(|s| s.id != newest_id);
2667 }
2668 }
2669 selections.push(Selection {
2670 id: post_inc(&mut self.next_selection_id),
2671 start: next_selected_range.start,
2672 end: next_selected_range.end,
2673 reversed: false,
2674 goal: SelectionGoal::None,
2675 });
2676 self.update_selections(selections, Some(Autoscroll::Newest), cx);
2677 } else {
2678 select_next_state.done = true;
2679 }
2680 }
2681
2682 self.select_next_state = Some(select_next_state);
2683 } else if selections.len() == 1 {
2684 let selection = selections.last_mut().unwrap();
2685 if selection.start == selection.end {
2686 let word_range = movement::surrounding_word(
2687 &display_map,
2688 selection.start.to_display_point(&display_map),
2689 );
2690 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
2691 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
2692 selection.goal = SelectionGoal::None;
2693 selection.reversed = false;
2694
2695 let query = buffer
2696 .text_for_range(selection.start..selection.end)
2697 .collect::<String>();
2698 let select_state = SelectNextState {
2699 query: AhoCorasick::new_auto_configured(&[query]),
2700 wordwise: true,
2701 done: false,
2702 };
2703 self.update_selections(selections, Some(Autoscroll::Newest), cx);
2704 self.select_next_state = Some(select_state);
2705 } else {
2706 let query = buffer
2707 .text_for_range(selection.start..selection.end)
2708 .collect::<String>();
2709 self.select_next_state = Some(SelectNextState {
2710 query: AhoCorasick::new_auto_configured(&[query]),
2711 wordwise: false,
2712 done: false,
2713 });
2714 self.select_next(action, cx);
2715 }
2716 }
2717 }
2718
2719 pub fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext<Self>) {
2720 // Get the line comment prefix. Split its trailing whitespace into a separate string,
2721 // as that portion won't be used for detecting if a line is a comment.
2722 let full_comment_prefix =
2723 if let Some(prefix) = self.language(cx).and_then(|l| l.line_comment_prefix()) {
2724 prefix.to_string()
2725 } else {
2726 return;
2727 };
2728 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
2729 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
2730
2731 self.start_transaction(cx);
2732 let mut selections = self.local_selections::<Point>(cx);
2733 let mut all_selection_lines_are_comments = true;
2734 let mut edit_ranges = Vec::new();
2735 let mut last_toggled_row = None;
2736 self.buffer.update(cx, |buffer, cx| {
2737 for selection in &mut selections {
2738 edit_ranges.clear();
2739 let snapshot = buffer.snapshot(cx);
2740
2741 let end_row =
2742 if selection.end.row > selection.start.row && selection.end.column == 0 {
2743 selection.end.row
2744 } else {
2745 selection.end.row + 1
2746 };
2747
2748 for row in selection.start.row..end_row {
2749 // If multiple selections contain a given row, avoid processing that
2750 // row more than once.
2751 if last_toggled_row == Some(row) {
2752 continue;
2753 } else {
2754 last_toggled_row = Some(row);
2755 }
2756
2757 if snapshot.is_line_blank(row) {
2758 continue;
2759 }
2760
2761 let start = Point::new(row, snapshot.indent_column_for_line(row));
2762 let mut line_bytes = snapshot
2763 .bytes_in_range(start..snapshot.max_point())
2764 .flatten()
2765 .copied();
2766
2767 // If this line currently begins with the line comment prefix, then record
2768 // the range containing the prefix.
2769 if all_selection_lines_are_comments
2770 && line_bytes
2771 .by_ref()
2772 .take(comment_prefix.len())
2773 .eq(comment_prefix.bytes())
2774 {
2775 // Include any whitespace that matches the comment prefix.
2776 let matching_whitespace_len = line_bytes
2777 .zip(comment_prefix_whitespace.bytes())
2778 .take_while(|(a, b)| a == b)
2779 .count() as u32;
2780 let end = Point::new(
2781 row,
2782 start.column + comment_prefix.len() as u32 + matching_whitespace_len,
2783 );
2784 edit_ranges.push(start..end);
2785 }
2786 // If this line does not begin with the line comment prefix, then record
2787 // the position where the prefix should be inserted.
2788 else {
2789 all_selection_lines_are_comments = false;
2790 edit_ranges.push(start..start);
2791 }
2792 }
2793
2794 if !edit_ranges.is_empty() {
2795 if all_selection_lines_are_comments {
2796 buffer.edit(edit_ranges.iter().cloned(), "", cx);
2797 } else {
2798 let min_column = edit_ranges.iter().map(|r| r.start.column).min().unwrap();
2799 let edit_ranges = edit_ranges.iter().map(|range| {
2800 let position = Point::new(range.start.row, min_column);
2801 position..position
2802 });
2803 buffer.edit(edit_ranges, &full_comment_prefix, cx);
2804 }
2805 }
2806 }
2807 });
2808
2809 self.update_selections(
2810 self.local_selections::<usize>(cx),
2811 Some(Autoscroll::Fit),
2812 cx,
2813 );
2814 self.end_transaction(cx);
2815 }
2816
2817 pub fn select_larger_syntax_node(
2818 &mut self,
2819 _: &SelectLargerSyntaxNode,
2820 cx: &mut ViewContext<Self>,
2821 ) {
2822 let old_selections = self.local_selections::<usize>(cx).into_boxed_slice();
2823 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2824 let buffer = self.buffer.read(cx).snapshot(cx);
2825
2826 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
2827 let mut selected_larger_node = false;
2828 let new_selections = old_selections
2829 .iter()
2830 .map(|selection| {
2831 let old_range = selection.start..selection.end;
2832 let mut new_range = old_range.clone();
2833 while let Some(containing_range) =
2834 buffer.range_for_syntax_ancestor(new_range.clone())
2835 {
2836 new_range = containing_range;
2837 if !display_map.intersects_fold(new_range.start)
2838 && !display_map.intersects_fold(new_range.end)
2839 {
2840 break;
2841 }
2842 }
2843
2844 selected_larger_node |= new_range != old_range;
2845 Selection {
2846 id: selection.id,
2847 start: new_range.start,
2848 end: new_range.end,
2849 goal: SelectionGoal::None,
2850 reversed: selection.reversed,
2851 }
2852 })
2853 .collect::<Vec<_>>();
2854
2855 if selected_larger_node {
2856 stack.push(old_selections);
2857 self.update_selections(new_selections, Some(Autoscroll::Fit), cx);
2858 }
2859 self.select_larger_syntax_node_stack = stack;
2860 }
2861
2862 pub fn select_smaller_syntax_node(
2863 &mut self,
2864 _: &SelectSmallerSyntaxNode,
2865 cx: &mut ViewContext<Self>,
2866 ) {
2867 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
2868 if let Some(selections) = stack.pop() {
2869 self.update_selections(selections.to_vec(), Some(Autoscroll::Fit), cx);
2870 }
2871 self.select_larger_syntax_node_stack = stack;
2872 }
2873
2874 pub fn move_to_enclosing_bracket(
2875 &mut self,
2876 _: &MoveToEnclosingBracket,
2877 cx: &mut ViewContext<Self>,
2878 ) {
2879 let mut selections = self.local_selections::<usize>(cx);
2880 let buffer = self.buffer.read(cx).snapshot(cx);
2881 for selection in &mut selections {
2882 if let Some((open_range, close_range)) =
2883 buffer.enclosing_bracket_ranges(selection.start..selection.end)
2884 {
2885 let close_range = close_range.to_inclusive();
2886 let destination = if close_range.contains(&selection.start)
2887 && close_range.contains(&selection.end)
2888 {
2889 open_range.end
2890 } else {
2891 *close_range.start()
2892 };
2893 selection.start = destination;
2894 selection.end = destination;
2895 }
2896 }
2897
2898 self.update_selections(selections, Some(Autoscroll::Fit), cx);
2899 }
2900
2901 pub fn show_next_diagnostic(&mut self, _: &ShowNextDiagnostic, cx: &mut ViewContext<Self>) {
2902 let buffer = self.buffer.read(cx).snapshot(cx);
2903 let selection = self.newest_selection::<usize>(&buffer);
2904 let active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
2905 active_diagnostics
2906 .primary_range
2907 .to_offset(&buffer)
2908 .to_inclusive()
2909 });
2910 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
2911 if active_primary_range.contains(&selection.head()) {
2912 *active_primary_range.end()
2913 } else {
2914 selection.head()
2915 }
2916 } else {
2917 selection.head()
2918 };
2919
2920 loop {
2921 let next_group = buffer
2922 .diagnostics_in_range::<_, usize>(search_start..buffer.len())
2923 .find_map(|entry| {
2924 if entry.diagnostic.is_primary
2925 && !entry.range.is_empty()
2926 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
2927 {
2928 Some((entry.range, entry.diagnostic.group_id))
2929 } else {
2930 None
2931 }
2932 });
2933
2934 if let Some((primary_range, group_id)) = next_group {
2935 self.activate_diagnostics(group_id, cx);
2936 self.update_selections(
2937 vec![Selection {
2938 id: selection.id,
2939 start: primary_range.start,
2940 end: primary_range.start,
2941 reversed: false,
2942 goal: SelectionGoal::None,
2943 }],
2944 Some(Autoscroll::Center),
2945 cx,
2946 );
2947 break;
2948 } else if search_start == 0 {
2949 break;
2950 } else {
2951 // Cycle around to the start of the buffer.
2952 search_start = 0;
2953 }
2954 }
2955 }
2956
2957 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
2958 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
2959 let buffer = self.buffer.read(cx).snapshot(cx);
2960 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
2961 let is_valid = buffer
2962 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone())
2963 .any(|entry| {
2964 entry.diagnostic.is_primary
2965 && !entry.range.is_empty()
2966 && entry.range.start == primary_range_start
2967 && entry.diagnostic.message == active_diagnostics.primary_message
2968 });
2969
2970 if is_valid != active_diagnostics.is_valid {
2971 active_diagnostics.is_valid = is_valid;
2972 let mut new_styles = HashMap::default();
2973 for (block_id, diagnostic) in &active_diagnostics.blocks {
2974 new_styles.insert(
2975 *block_id,
2976 diagnostic_block_renderer(
2977 diagnostic.clone(),
2978 is_valid,
2979 self.build_settings.clone(),
2980 ),
2981 );
2982 }
2983 self.display_map
2984 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
2985 }
2986 }
2987 }
2988
2989 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) {
2990 self.dismiss_diagnostics(cx);
2991 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
2992 let buffer = self.buffer.read(cx).snapshot(cx);
2993
2994 let mut primary_range = None;
2995 let mut primary_message = None;
2996 let mut group_end = Point::zero();
2997 let diagnostic_group = buffer
2998 .diagnostic_group::<Point>(group_id)
2999 .map(|entry| {
3000 if entry.range.end > group_end {
3001 group_end = entry.range.end;
3002 }
3003 if entry.diagnostic.is_primary {
3004 primary_range = Some(entry.range.clone());
3005 primary_message = Some(entry.diagnostic.message.clone());
3006 }
3007 entry
3008 })
3009 .collect::<Vec<_>>();
3010 let primary_range = primary_range.unwrap();
3011 let primary_message = primary_message.unwrap();
3012 let primary_range =
3013 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
3014
3015 let blocks = display_map
3016 .insert_blocks(
3017 diagnostic_group.iter().map(|entry| {
3018 let build_settings = self.build_settings.clone();
3019 let diagnostic = entry.diagnostic.clone();
3020 let message_height = diagnostic.message.lines().count() as u8;
3021
3022 BlockProperties {
3023 position: buffer.anchor_after(entry.range.start),
3024 height: message_height,
3025 render: diagnostic_block_renderer(diagnostic, true, build_settings),
3026 disposition: BlockDisposition::Below,
3027 }
3028 }),
3029 cx,
3030 )
3031 .into_iter()
3032 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
3033 .collect();
3034
3035 Some(ActiveDiagnosticGroup {
3036 primary_range,
3037 primary_message,
3038 blocks,
3039 is_valid: true,
3040 })
3041 });
3042 }
3043
3044 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
3045 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
3046 self.display_map.update(cx, |display_map, cx| {
3047 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
3048 });
3049 cx.notify();
3050 }
3051 }
3052
3053 fn build_columnar_selection(
3054 &mut self,
3055 display_map: &DisplaySnapshot,
3056 row: u32,
3057 columns: &Range<u32>,
3058 reversed: bool,
3059 ) -> Option<Selection<Point>> {
3060 let is_empty = columns.start == columns.end;
3061 let line_len = display_map.line_len(row);
3062 if columns.start < line_len || (is_empty && columns.start == line_len) {
3063 let start = DisplayPoint::new(row, columns.start);
3064 let end = DisplayPoint::new(row, cmp::min(columns.end, line_len));
3065 Some(Selection {
3066 id: post_inc(&mut self.next_selection_id),
3067 start: start.to_point(display_map),
3068 end: end.to_point(display_map),
3069 reversed,
3070 goal: SelectionGoal::ColumnRange {
3071 start: columns.start,
3072 end: columns.end,
3073 },
3074 })
3075 } else {
3076 None
3077 }
3078 }
3079
3080 pub fn local_selections_in_range(
3081 &self,
3082 range: Range<Anchor>,
3083 display_map: &DisplaySnapshot,
3084 ) -> Vec<Selection<Point>> {
3085 let buffer = &display_map.buffer_snapshot;
3086
3087 let start_ix = match self
3088 .selections
3089 .binary_search_by(|probe| probe.end.cmp(&range.start, &buffer).unwrap())
3090 {
3091 Ok(ix) | Err(ix) => ix,
3092 };
3093 let end_ix = match self
3094 .selections
3095 .binary_search_by(|probe| probe.start.cmp(&range.end, &buffer).unwrap())
3096 {
3097 Ok(ix) => ix + 1,
3098 Err(ix) => ix,
3099 };
3100
3101 fn point_selection(
3102 selection: &Selection<Anchor>,
3103 buffer: &MultiBufferSnapshot,
3104 ) -> Selection<Point> {
3105 let start = selection.start.to_point(&buffer);
3106 let end = selection.end.to_point(&buffer);
3107 Selection {
3108 id: selection.id,
3109 start,
3110 end,
3111 reversed: selection.reversed,
3112 goal: selection.goal,
3113 }
3114 }
3115
3116 self.selections[start_ix..end_ix]
3117 .iter()
3118 .chain(
3119 self.pending_selection
3120 .as_ref()
3121 .map(|pending| &pending.selection),
3122 )
3123 .map(|s| point_selection(s, &buffer))
3124 .collect()
3125 }
3126
3127 pub fn local_selections<'a, D>(&self, cx: &'a AppContext) -> Vec<Selection<D>>
3128 where
3129 D: 'a + TextDimension + Ord + Sub<D, Output = D>,
3130 {
3131 let buffer = self.buffer.read(cx).snapshot(cx);
3132 let mut selections = self
3133 .resolve_selections::<D, _>(self.selections.iter(), &buffer)
3134 .peekable();
3135
3136 let mut pending_selection = self.pending_selection::<D>(&buffer);
3137
3138 iter::from_fn(move || {
3139 if let Some(pending) = pending_selection.as_mut() {
3140 while let Some(next_selection) = selections.peek() {
3141 if pending.start <= next_selection.end && pending.end >= next_selection.start {
3142 let next_selection = selections.next().unwrap();
3143 if next_selection.start < pending.start {
3144 pending.start = next_selection.start;
3145 }
3146 if next_selection.end > pending.end {
3147 pending.end = next_selection.end;
3148 }
3149 } else if next_selection.end < pending.start {
3150 return selections.next();
3151 } else {
3152 break;
3153 }
3154 }
3155
3156 pending_selection.take()
3157 } else {
3158 selections.next()
3159 }
3160 })
3161 .collect()
3162 }
3163
3164 fn resolve_selections<'a, D, I>(
3165 &self,
3166 selections: I,
3167 snapshot: &MultiBufferSnapshot,
3168 ) -> impl 'a + Iterator<Item = Selection<D>>
3169 where
3170 D: TextDimension + Ord + Sub<D, Output = D>,
3171 I: 'a + IntoIterator<Item = &'a Selection<Anchor>>,
3172 {
3173 let (to_summarize, selections) = selections.into_iter().tee();
3174 let mut summaries = snapshot
3175 .summaries_for_anchors::<D, _>(to_summarize.flat_map(|s| [&s.start, &s.end]))
3176 .into_iter();
3177 selections.map(move |s| Selection {
3178 id: s.id,
3179 start: summaries.next().unwrap(),
3180 end: summaries.next().unwrap(),
3181 reversed: s.reversed,
3182 goal: s.goal,
3183 })
3184 }
3185
3186 fn pending_selection<D: TextDimension + Ord + Sub<D, Output = D>>(
3187 &self,
3188 snapshot: &MultiBufferSnapshot,
3189 ) -> Option<Selection<D>> {
3190 self.pending_selection
3191 .as_ref()
3192 .map(|pending| self.resolve_selection(&pending.selection, &snapshot))
3193 }
3194
3195 fn resolve_selection<D: TextDimension + Ord + Sub<D, Output = D>>(
3196 &self,
3197 selection: &Selection<Anchor>,
3198 buffer: &MultiBufferSnapshot,
3199 ) -> Selection<D> {
3200 Selection {
3201 id: selection.id,
3202 start: selection.start.summary::<D>(&buffer),
3203 end: selection.end.summary::<D>(&buffer),
3204 reversed: selection.reversed,
3205 goal: selection.goal,
3206 }
3207 }
3208
3209 fn selection_count<'a>(&self) -> usize {
3210 let mut count = self.selections.len();
3211 if self.pending_selection.is_some() {
3212 count += 1;
3213 }
3214 count
3215 }
3216
3217 pub fn oldest_selection<D: TextDimension + Ord + Sub<D, Output = D>>(
3218 &self,
3219 snapshot: &MultiBufferSnapshot,
3220 ) -> Selection<D> {
3221 self.selections
3222 .iter()
3223 .min_by_key(|s| s.id)
3224 .map(|selection| self.resolve_selection(selection, snapshot))
3225 .or_else(|| self.pending_selection(snapshot))
3226 .unwrap()
3227 }
3228
3229 pub fn newest_selection<D: TextDimension + Ord + Sub<D, Output = D>>(
3230 &self,
3231 snapshot: &MultiBufferSnapshot,
3232 ) -> Selection<D> {
3233 self.pending_selection(snapshot)
3234 .or_else(|| {
3235 self.selections
3236 .iter()
3237 .max_by_key(|s| s.id)
3238 .map(|selection| self.resolve_selection(selection, snapshot))
3239 })
3240 .unwrap()
3241 }
3242
3243 pub fn update_selections<T>(
3244 &mut self,
3245 mut selections: Vec<Selection<T>>,
3246 autoscroll: Option<Autoscroll>,
3247 cx: &mut ViewContext<Self>,
3248 ) where
3249 T: ToOffset + ToPoint + Ord + std::marker::Copy + std::fmt::Debug,
3250 {
3251 selections.sort_unstable_by_key(|s| s.start);
3252
3253 // Merge overlapping selections.
3254 let buffer = self.buffer.read(cx).snapshot(cx);
3255 let mut i = 1;
3256 while i < selections.len() {
3257 if selections[i - 1].end >= selections[i].start {
3258 let removed = selections.remove(i);
3259 if removed.start < selections[i - 1].start {
3260 selections[i - 1].start = removed.start;
3261 }
3262 if removed.end > selections[i - 1].end {
3263 selections[i - 1].end = removed.end;
3264 }
3265 } else {
3266 i += 1;
3267 }
3268 }
3269
3270 self.pending_selection = None;
3271 self.add_selections_state = None;
3272 self.select_next_state = None;
3273 self.select_larger_syntax_node_stack.clear();
3274 while let Some(autoclose_pair) = self.autoclose_stack.last() {
3275 let all_selections_inside_autoclose_ranges =
3276 if selections.len() == autoclose_pair.ranges.len() {
3277 selections
3278 .iter()
3279 .zip(autoclose_pair.ranges.iter().map(|r| r.to_point(&buffer)))
3280 .all(|(selection, autoclose_range)| {
3281 let head = selection.head().to_point(&buffer);
3282 autoclose_range.start <= head && autoclose_range.end >= head
3283 })
3284 } else {
3285 false
3286 };
3287
3288 if all_selections_inside_autoclose_ranges {
3289 break;
3290 } else {
3291 self.autoclose_stack.pop();
3292 }
3293 }
3294
3295 if let Some(autoscroll) = autoscroll {
3296 self.request_autoscroll(autoscroll, cx);
3297 }
3298 self.pause_cursor_blinking(cx);
3299
3300 self.set_selections(
3301 Arc::from_iter(selections.into_iter().map(|selection| {
3302 let end_bias = if selection.end > selection.start {
3303 Bias::Left
3304 } else {
3305 Bias::Right
3306 };
3307 Selection {
3308 id: selection.id,
3309 start: buffer.anchor_after(selection.start),
3310 end: buffer.anchor_at(selection.end, end_bias),
3311 reversed: selection.reversed,
3312 goal: selection.goal,
3313 }
3314 })),
3315 cx,
3316 );
3317 }
3318
3319 /// Compute new ranges for any selections that were located in excerpts that have
3320 /// since been removed.
3321 ///
3322 /// Returns a `HashMap` indicating which selections whose former head position
3323 /// was no longer present. The keys of the map are selection ids. The values are
3324 /// the id of the new excerpt where the head of the selection has been moved.
3325 pub fn refresh_selections(&mut self, cx: &mut ViewContext<Self>) -> HashMap<usize, ExcerptId> {
3326 let snapshot = self.buffer.read(cx).read(cx);
3327 let anchors_with_status = snapshot.refresh_anchors(
3328 self.selections
3329 .iter()
3330 .flat_map(|selection| [&selection.start, &selection.end]),
3331 );
3332 let offsets =
3333 snapshot.summaries_for_anchors::<usize, _>(anchors_with_status.iter().map(|a| &a.1));
3334 let offsets = offsets.chunks(2);
3335 let statuses = anchors_with_status
3336 .chunks(2)
3337 .map(|a| (a[0].0 / 2, a[0].2, a[1].2));
3338
3339 let mut selections_with_lost_position = HashMap::default();
3340 let new_selections = offsets
3341 .zip(statuses)
3342 .map(|(offsets, (selection_ix, kept_start, kept_end))| {
3343 let selection = &self.selections[selection_ix];
3344 let kept_head = if selection.reversed {
3345 kept_start
3346 } else {
3347 kept_end
3348 };
3349 if !kept_head {
3350 selections_with_lost_position
3351 .insert(selection.id, selection.head().excerpt_id.clone());
3352 }
3353
3354 Selection {
3355 id: selection.id,
3356 start: offsets[0],
3357 end: offsets[1],
3358 reversed: selection.reversed,
3359 goal: selection.goal,
3360 }
3361 })
3362 .collect();
3363 drop(snapshot);
3364 self.update_selections(new_selections, Some(Autoscroll::Fit), cx);
3365 selections_with_lost_position
3366 }
3367
3368 fn set_selections(&mut self, selections: Arc<[Selection<Anchor>]>, cx: &mut ViewContext<Self>) {
3369 self.selections = selections;
3370 self.buffer.update(cx, |buffer, cx| {
3371 buffer.set_active_selections(&self.selections, cx)
3372 });
3373 }
3374
3375 fn request_autoscroll(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
3376 self.autoscroll_request = Some(autoscroll);
3377 cx.notify();
3378 }
3379
3380 fn start_transaction(&mut self, cx: &mut ViewContext<Self>) {
3381 self.start_transaction_at(Instant::now(), cx);
3382 }
3383
3384 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
3385 self.end_selection(cx);
3386 if let Some(tx_id) = self
3387 .buffer
3388 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
3389 {
3390 self.selection_history
3391 .insert(tx_id, (self.selections.clone(), None));
3392 }
3393 }
3394
3395 fn end_transaction(&mut self, cx: &mut ViewContext<Self>) {
3396 self.end_transaction_at(Instant::now(), cx);
3397 }
3398
3399 fn end_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
3400 if let Some(tx_id) = self
3401 .buffer
3402 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
3403 {
3404 self.selection_history.get_mut(&tx_id).unwrap().1 = Some(self.selections.clone());
3405 }
3406 }
3407
3408 pub fn page_up(&mut self, _: &PageUp, _: &mut ViewContext<Self>) {
3409 log::info!("Editor::page_up");
3410 }
3411
3412 pub fn page_down(&mut self, _: &PageDown, _: &mut ViewContext<Self>) {
3413 log::info!("Editor::page_down");
3414 }
3415
3416 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
3417 let mut fold_ranges = Vec::new();
3418
3419 let selections = self.local_selections::<Point>(cx);
3420 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3421 for selection in selections {
3422 let range = selection.display_range(&display_map).sorted();
3423 let buffer_start_row = range.start.to_point(&display_map).row;
3424
3425 for row in (0..=range.end.row()).rev() {
3426 if self.is_line_foldable(&display_map, row) && !display_map.is_line_folded(row) {
3427 let fold_range = self.foldable_range_for_line(&display_map, row);
3428 if fold_range.end.row >= buffer_start_row {
3429 fold_ranges.push(fold_range);
3430 if row <= range.start.row() {
3431 break;
3432 }
3433 }
3434 }
3435 }
3436 }
3437
3438 self.fold_ranges(fold_ranges, cx);
3439 }
3440
3441 pub fn unfold(&mut self, _: &Unfold, cx: &mut ViewContext<Self>) {
3442 let selections = self.local_selections::<Point>(cx);
3443 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3444 let buffer = &display_map.buffer_snapshot;
3445 let ranges = selections
3446 .iter()
3447 .map(|s| {
3448 let range = s.display_range(&display_map).sorted();
3449 let mut start = range.start.to_point(&display_map);
3450 let mut end = range.end.to_point(&display_map);
3451 start.column = 0;
3452 end.column = buffer.line_len(end.row);
3453 start..end
3454 })
3455 .collect::<Vec<_>>();
3456 self.unfold_ranges(ranges, cx);
3457 }
3458
3459 fn is_line_foldable(&self, display_map: &DisplaySnapshot, display_row: u32) -> bool {
3460 let max_point = display_map.max_point();
3461 if display_row >= max_point.row() {
3462 false
3463 } else {
3464 let (start_indent, is_blank) = display_map.line_indent(display_row);
3465 if is_blank {
3466 false
3467 } else {
3468 for display_row in display_row + 1..=max_point.row() {
3469 let (indent, is_blank) = display_map.line_indent(display_row);
3470 if !is_blank {
3471 return indent > start_indent;
3472 }
3473 }
3474 false
3475 }
3476 }
3477 }
3478
3479 fn foldable_range_for_line(
3480 &self,
3481 display_map: &DisplaySnapshot,
3482 start_row: u32,
3483 ) -> Range<Point> {
3484 let max_point = display_map.max_point();
3485
3486 let (start_indent, _) = display_map.line_indent(start_row);
3487 let start = DisplayPoint::new(start_row, display_map.line_len(start_row));
3488 let mut end = None;
3489 for row in start_row + 1..=max_point.row() {
3490 let (indent, is_blank) = display_map.line_indent(row);
3491 if !is_blank && indent <= start_indent {
3492 end = Some(DisplayPoint::new(row - 1, display_map.line_len(row - 1)));
3493 break;
3494 }
3495 }
3496
3497 let end = end.unwrap_or(max_point);
3498 return start.to_point(display_map)..end.to_point(display_map);
3499 }
3500
3501 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
3502 let selections = self.local_selections::<Point>(cx);
3503 let ranges = selections.into_iter().map(|s| s.start..s.end);
3504 self.fold_ranges(ranges, cx);
3505 }
3506
3507 fn fold_ranges<T: ToOffset>(
3508 &mut self,
3509 ranges: impl IntoIterator<Item = Range<T>>,
3510 cx: &mut ViewContext<Self>,
3511 ) {
3512 let mut ranges = ranges.into_iter().peekable();
3513 if ranges.peek().is_some() {
3514 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
3515 self.request_autoscroll(Autoscroll::Fit, cx);
3516 cx.notify();
3517 }
3518 }
3519
3520 fn unfold_ranges<T: ToOffset>(&mut self, ranges: Vec<Range<T>>, cx: &mut ViewContext<Self>) {
3521 if !ranges.is_empty() {
3522 self.display_map
3523 .update(cx, |map, cx| map.unfold(ranges, cx));
3524 self.request_autoscroll(Autoscroll::Fit, cx);
3525 cx.notify();
3526 }
3527 }
3528
3529 pub fn insert_blocks(
3530 &mut self,
3531 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
3532 cx: &mut ViewContext<Self>,
3533 ) -> Vec<BlockId> {
3534 let blocks = self
3535 .display_map
3536 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
3537 self.request_autoscroll(Autoscroll::Fit, cx);
3538 blocks
3539 }
3540
3541 pub fn replace_blocks(
3542 &mut self,
3543 blocks: HashMap<BlockId, RenderBlock>,
3544 cx: &mut ViewContext<Self>,
3545 ) {
3546 self.display_map
3547 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
3548 self.request_autoscroll(Autoscroll::Fit, cx);
3549 }
3550
3551 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
3552 self.display_map.update(cx, |display_map, cx| {
3553 display_map.remove_blocks(block_ids, cx)
3554 });
3555 }
3556
3557 pub fn longest_row(&self, cx: &mut MutableAppContext) -> u32 {
3558 self.display_map
3559 .update(cx, |map, cx| map.snapshot(cx))
3560 .longest_row()
3561 }
3562
3563 pub fn max_point(&self, cx: &mut MutableAppContext) -> DisplayPoint {
3564 self.display_map
3565 .update(cx, |map, cx| map.snapshot(cx))
3566 .max_point()
3567 }
3568
3569 pub fn text(&self, cx: &AppContext) -> String {
3570 self.buffer.read(cx).read(cx).text()
3571 }
3572
3573 pub fn display_text(&self, cx: &mut MutableAppContext) -> String {
3574 self.display_map
3575 .update(cx, |map, cx| map.snapshot(cx))
3576 .text()
3577 }
3578
3579 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut MutableAppContext) -> bool {
3580 self.display_map
3581 .update(cx, |map, cx| map.set_wrap_width(width, cx))
3582 }
3583
3584 pub fn set_highlighted_rows(&mut self, rows: Option<Range<u32>>) {
3585 self.highlighted_rows = rows;
3586 }
3587
3588 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
3589 self.highlighted_rows.clone()
3590 }
3591
3592 fn next_blink_epoch(&mut self) -> usize {
3593 self.blink_epoch += 1;
3594 self.blink_epoch
3595 }
3596
3597 fn pause_cursor_blinking(&mut self, cx: &mut ViewContext<Self>) {
3598 self.show_local_cursors = true;
3599 cx.notify();
3600
3601 let epoch = self.next_blink_epoch();
3602 cx.spawn(|this, mut cx| {
3603 let this = this.downgrade();
3604 async move {
3605 Timer::after(CURSOR_BLINK_INTERVAL).await;
3606 if let Some(this) = cx.read(|cx| this.upgrade(cx)) {
3607 this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
3608 }
3609 }
3610 })
3611 .detach();
3612 }
3613
3614 fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
3615 if epoch == self.blink_epoch {
3616 self.blinking_paused = false;
3617 self.blink_cursors(epoch, cx);
3618 }
3619 }
3620
3621 fn blink_cursors(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
3622 if epoch == self.blink_epoch && self.focused && !self.blinking_paused {
3623 self.show_local_cursors = !self.show_local_cursors;
3624 cx.notify();
3625
3626 let epoch = self.next_blink_epoch();
3627 cx.spawn(|this, mut cx| {
3628 let this = this.downgrade();
3629 async move {
3630 Timer::after(CURSOR_BLINK_INTERVAL).await;
3631 if let Some(this) = cx.read(|cx| this.upgrade(cx)) {
3632 this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx));
3633 }
3634 }
3635 })
3636 .detach();
3637 }
3638 }
3639
3640 pub fn show_local_cursors(&self) -> bool {
3641 self.show_local_cursors
3642 }
3643
3644 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
3645 self.refresh_active_diagnostics(cx);
3646 cx.notify();
3647 }
3648
3649 fn on_buffer_event(
3650 &mut self,
3651 _: ModelHandle<MultiBuffer>,
3652 event: &language::Event,
3653 cx: &mut ViewContext<Self>,
3654 ) {
3655 match event {
3656 language::Event::Edited => cx.emit(Event::Edited),
3657 language::Event::Dirtied => cx.emit(Event::Dirtied),
3658 language::Event::Saved => cx.emit(Event::Saved),
3659 language::Event::FileHandleChanged => cx.emit(Event::FileHandleChanged),
3660 language::Event::Reloaded => cx.emit(Event::FileHandleChanged),
3661 language::Event::Closed => cx.emit(Event::Closed),
3662 _ => {}
3663 }
3664 }
3665
3666 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
3667 cx.notify();
3668 }
3669}
3670
3671impl EditorSnapshot {
3672 pub fn is_focused(&self) -> bool {
3673 self.is_focused
3674 }
3675
3676 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
3677 self.placeholder_text.as_ref()
3678 }
3679
3680 pub fn scroll_position(&self) -> Vector2F {
3681 compute_scroll_position(
3682 &self.display_snapshot,
3683 self.scroll_position,
3684 &self.scroll_top_anchor,
3685 )
3686 }
3687}
3688
3689impl Deref for EditorSnapshot {
3690 type Target = DisplaySnapshot;
3691
3692 fn deref(&self) -> &Self::Target {
3693 &self.display_snapshot
3694 }
3695}
3696
3697impl EditorSettings {
3698 #[cfg(any(test, feature = "test-support"))]
3699 pub fn test(cx: &AppContext) -> Self {
3700 Self {
3701 tab_size: 4,
3702 soft_wrap: SoftWrap::None,
3703 style: {
3704 let font_cache: &gpui::FontCache = cx.font_cache();
3705 let font_family_name = Arc::from("Monaco");
3706 let font_properties = Default::default();
3707 let font_family_id = font_cache.load_family(&[&font_family_name]).unwrap();
3708 let font_id = font_cache
3709 .select_font(font_family_id, &font_properties)
3710 .unwrap();
3711 EditorStyle {
3712 text: gpui::fonts::TextStyle {
3713 font_family_name,
3714 font_family_id,
3715 font_id,
3716 font_size: 14.,
3717 color: gpui::color::Color::from_u32(0xff0000ff),
3718 font_properties,
3719 underline: None,
3720 },
3721 placeholder_text: None,
3722 background: Default::default(),
3723 gutter_background: Default::default(),
3724 active_line_background: Default::default(),
3725 highlighted_line_background: Default::default(),
3726 line_number: Default::default(),
3727 line_number_active: Default::default(),
3728 selection: Default::default(),
3729 guest_selections: Default::default(),
3730 syntax: Default::default(),
3731 diagnostic_path_header: Default::default(),
3732 error_diagnostic: Default::default(),
3733 invalid_error_diagnostic: Default::default(),
3734 warning_diagnostic: Default::default(),
3735 invalid_warning_diagnostic: Default::default(),
3736 information_diagnostic: Default::default(),
3737 invalid_information_diagnostic: Default::default(),
3738 hint_diagnostic: Default::default(),
3739 invalid_hint_diagnostic: Default::default(),
3740 }
3741 },
3742 }
3743 }
3744}
3745
3746fn compute_scroll_position(
3747 snapshot: &DisplaySnapshot,
3748 mut scroll_position: Vector2F,
3749 scroll_top_anchor: &Option<Anchor>,
3750) -> Vector2F {
3751 if let Some(anchor) = scroll_top_anchor {
3752 let scroll_top = anchor.to_display_point(snapshot).row() as f32;
3753 scroll_position.set_y(scroll_top + scroll_position.y());
3754 } else {
3755 scroll_position.set_y(0.);
3756 }
3757 scroll_position
3758}
3759
3760#[derive(Copy, Clone)]
3761pub enum Event {
3762 Activate,
3763 Edited,
3764 Blurred,
3765 Dirtied,
3766 Saved,
3767 FileHandleChanged,
3768 Closed,
3769}
3770
3771impl Entity for Editor {
3772 type Event = Event;
3773}
3774
3775impl View for Editor {
3776 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
3777 let settings = (self.build_settings)(cx);
3778 self.display_map.update(cx, |map, cx| {
3779 map.set_font(
3780 settings.style.text.font_id,
3781 settings.style.text.font_size,
3782 cx,
3783 )
3784 });
3785 EditorElement::new(self.handle.clone(), settings).boxed()
3786 }
3787
3788 fn ui_name() -> &'static str {
3789 "Editor"
3790 }
3791
3792 fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
3793 self.focused = true;
3794 self.blink_cursors(self.blink_epoch, cx);
3795 self.buffer.update(cx, |buffer, cx| {
3796 buffer.set_active_selections(&self.selections, cx)
3797 });
3798 }
3799
3800 fn on_blur(&mut self, cx: &mut ViewContext<Self>) {
3801 self.focused = false;
3802 self.show_local_cursors = false;
3803 self.buffer
3804 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
3805 cx.emit(Event::Blurred);
3806 cx.notify();
3807 }
3808
3809 fn keymap_context(&self, _: &AppContext) -> gpui::keymap::Context {
3810 let mut cx = Self::default_keymap_context();
3811 let mode = match self.mode {
3812 EditorMode::SingleLine => "single_line",
3813 EditorMode::AutoHeight { .. } => "auto_height",
3814 EditorMode::Full => "full",
3815 };
3816 cx.map.insert("mode".into(), mode.into());
3817 cx
3818 }
3819}
3820
3821impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
3822 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
3823 let start = self.start.to_point(buffer);
3824 let end = self.end.to_point(buffer);
3825 if self.reversed {
3826 end..start
3827 } else {
3828 start..end
3829 }
3830 }
3831
3832 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
3833 let start = self.start.to_offset(buffer);
3834 let end = self.end.to_offset(buffer);
3835 if self.reversed {
3836 end..start
3837 } else {
3838 start..end
3839 }
3840 }
3841
3842 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
3843 let start = self
3844 .start
3845 .to_point(&map.buffer_snapshot)
3846 .to_display_point(map);
3847 let end = self
3848 .end
3849 .to_point(&map.buffer_snapshot)
3850 .to_display_point(map);
3851 if self.reversed {
3852 end..start
3853 } else {
3854 start..end
3855 }
3856 }
3857
3858 fn spanned_rows(
3859 &self,
3860 include_end_if_at_line_start: bool,
3861 map: &DisplaySnapshot,
3862 ) -> Range<u32> {
3863 let start = self.start.to_point(&map.buffer_snapshot);
3864 let mut end = self.end.to_point(&map.buffer_snapshot);
3865 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
3866 end.row -= 1;
3867 }
3868
3869 let buffer_start = map.prev_line_boundary(start).0;
3870 let buffer_end = map.next_line_boundary(end).0;
3871 buffer_start.row..buffer_end.row + 1
3872 }
3873}
3874
3875pub fn diagnostic_block_renderer(
3876 diagnostic: Diagnostic,
3877 is_valid: bool,
3878 build_settings: BuildSettings,
3879) -> RenderBlock {
3880 Arc::new(move |cx: &BlockContext| {
3881 let settings = build_settings(cx);
3882 let mut text_style = settings.style.text.clone();
3883 text_style.color = diagnostic_style(diagnostic.severity, is_valid, &settings.style).text;
3884 Text::new(diagnostic.message.clone(), text_style)
3885 .with_soft_wrap(false)
3886 .contained()
3887 .with_margin_left(cx.anchor_x)
3888 .boxed()
3889 })
3890}
3891
3892pub fn diagnostic_style(
3893 severity: DiagnosticSeverity,
3894 valid: bool,
3895 style: &EditorStyle,
3896) -> DiagnosticStyle {
3897 match (severity, valid) {
3898 (DiagnosticSeverity::ERROR, true) => style.error_diagnostic,
3899 (DiagnosticSeverity::ERROR, false) => style.invalid_error_diagnostic,
3900 (DiagnosticSeverity::WARNING, true) => style.warning_diagnostic,
3901 (DiagnosticSeverity::WARNING, false) => style.invalid_warning_diagnostic,
3902 (DiagnosticSeverity::INFORMATION, true) => style.information_diagnostic,
3903 (DiagnosticSeverity::INFORMATION, false) => style.invalid_information_diagnostic,
3904 (DiagnosticSeverity::HINT, true) => style.hint_diagnostic,
3905 (DiagnosticSeverity::HINT, false) => style.invalid_hint_diagnostic,
3906 _ => Default::default(),
3907 }
3908}
3909
3910pub fn settings_builder(
3911 buffer: WeakModelHandle<MultiBuffer>,
3912 settings: watch::Receiver<workspace::Settings>,
3913) -> BuildSettings {
3914 Arc::new(move |cx| {
3915 let settings = settings.borrow();
3916 let font_cache = cx.font_cache();
3917 let font_family_id = settings.buffer_font_family;
3918 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
3919 let font_properties = Default::default();
3920 let font_id = font_cache
3921 .select_font(font_family_id, &font_properties)
3922 .unwrap();
3923 let font_size = settings.buffer_font_size;
3924
3925 let mut theme = settings.theme.editor.clone();
3926 theme.text = TextStyle {
3927 color: theme.text.color,
3928 font_family_name,
3929 font_family_id,
3930 font_id,
3931 font_size,
3932 font_properties,
3933 underline: None,
3934 };
3935 let language = buffer.upgrade(cx).and_then(|buf| buf.read(cx).language(cx));
3936 let soft_wrap = match settings.soft_wrap(language) {
3937 workspace::settings::SoftWrap::None => SoftWrap::None,
3938 workspace::settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
3939 workspace::settings::SoftWrap::PreferredLineLength => {
3940 SoftWrap::Column(settings.preferred_line_length(language).saturating_sub(1))
3941 }
3942 };
3943
3944 EditorSettings {
3945 tab_size: settings.tab_size,
3946 soft_wrap,
3947 style: theme,
3948 }
3949 })
3950}
3951
3952#[cfg(test)]
3953mod tests {
3954 use super::*;
3955 use language::LanguageConfig;
3956 use std::time::Instant;
3957 use text::Point;
3958 use unindent::Unindent;
3959 use util::test::sample_text;
3960
3961 #[gpui::test]
3962 fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
3963 let mut now = Instant::now();
3964 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
3965 let group_interval = buffer.read(cx).transaction_group_interval();
3966 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3967 let settings = EditorSettings::test(cx);
3968 let (_, editor) = cx.add_window(Default::default(), |cx| {
3969 build_editor(buffer.clone(), settings, cx)
3970 });
3971
3972 editor.update(cx, |editor, cx| {
3973 editor.start_transaction_at(now, cx);
3974 editor.select_ranges([2..4], None, cx);
3975 editor.insert("cd", cx);
3976 editor.end_transaction_at(now, cx);
3977 assert_eq!(editor.text(cx), "12cd56");
3978 assert_eq!(editor.selected_ranges(cx), vec![4..4]);
3979
3980 editor.start_transaction_at(now, cx);
3981 editor.select_ranges([4..5], None, cx);
3982 editor.insert("e", cx);
3983 editor.end_transaction_at(now, cx);
3984 assert_eq!(editor.text(cx), "12cde6");
3985 assert_eq!(editor.selected_ranges(cx), vec![5..5]);
3986
3987 now += group_interval + Duration::from_millis(1);
3988 editor.select_ranges([2..2], None, cx);
3989
3990 // Simulate an edit in another editor
3991 buffer.update(cx, |buffer, cx| {
3992 buffer.start_transaction_at(now, cx);
3993 buffer.edit([0..1], "a", cx);
3994 buffer.edit([1..1], "b", cx);
3995 buffer.end_transaction_at(now, cx);
3996 });
3997
3998 assert_eq!(editor.text(cx), "ab2cde6");
3999 assert_eq!(editor.selected_ranges(cx), vec![3..3]);
4000
4001 // Last transaction happened past the group interval in a different editor.
4002 // Undo it individually and don't restore selections.
4003 editor.undo(&Undo, cx);
4004 assert_eq!(editor.text(cx), "12cde6");
4005 assert_eq!(editor.selected_ranges(cx), vec![2..2]);
4006
4007 // First two transactions happened within the group interval in this editor.
4008 // Undo them together and restore selections.
4009 editor.undo(&Undo, cx);
4010 editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
4011 assert_eq!(editor.text(cx), "123456");
4012 assert_eq!(editor.selected_ranges(cx), vec![0..0]);
4013
4014 // Redo the first two transactions together.
4015 editor.redo(&Redo, cx);
4016 assert_eq!(editor.text(cx), "12cde6");
4017 assert_eq!(editor.selected_ranges(cx), vec![5..5]);
4018
4019 // Redo the last transaction on its own.
4020 editor.redo(&Redo, cx);
4021 assert_eq!(editor.text(cx), "ab2cde6");
4022 assert_eq!(editor.selected_ranges(cx), vec![6..6]);
4023
4024 // Test empty transactions.
4025 editor.start_transaction_at(now, cx);
4026 editor.end_transaction_at(now, cx);
4027 editor.undo(&Undo, cx);
4028 assert_eq!(editor.text(cx), "12cde6");
4029 });
4030 }
4031
4032 #[gpui::test]
4033 fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
4034 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
4035 let settings = EditorSettings::test(cx);
4036 let (_, editor) =
4037 cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
4038
4039 editor.update(cx, |view, cx| {
4040 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
4041 });
4042
4043 assert_eq!(
4044 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
4045 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
4046 );
4047
4048 editor.update(cx, |view, cx| {
4049 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
4050 });
4051
4052 assert_eq!(
4053 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
4054 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
4055 );
4056
4057 editor.update(cx, |view, cx| {
4058 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
4059 });
4060
4061 assert_eq!(
4062 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
4063 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
4064 );
4065
4066 editor.update(cx, |view, cx| {
4067 view.end_selection(cx);
4068 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
4069 });
4070
4071 assert_eq!(
4072 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
4073 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
4074 );
4075
4076 editor.update(cx, |view, cx| {
4077 view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
4078 view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
4079 });
4080
4081 assert_eq!(
4082 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
4083 [
4084 DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
4085 DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
4086 ]
4087 );
4088
4089 editor.update(cx, |view, cx| {
4090 view.end_selection(cx);
4091 });
4092
4093 assert_eq!(
4094 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
4095 [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
4096 );
4097 }
4098
4099 #[gpui::test]
4100 fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
4101 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
4102 let settings = EditorSettings::test(cx);
4103 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
4104
4105 view.update(cx, |view, cx| {
4106 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
4107 assert_eq!(
4108 view.selected_display_ranges(cx),
4109 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
4110 );
4111 });
4112
4113 view.update(cx, |view, cx| {
4114 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
4115 assert_eq!(
4116 view.selected_display_ranges(cx),
4117 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
4118 );
4119 });
4120
4121 view.update(cx, |view, cx| {
4122 view.cancel(&Cancel, cx);
4123 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
4124 assert_eq!(
4125 view.selected_display_ranges(cx),
4126 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
4127 );
4128 });
4129 }
4130
4131 #[gpui::test]
4132 fn test_cancel(cx: &mut gpui::MutableAppContext) {
4133 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
4134 let settings = EditorSettings::test(cx);
4135 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
4136
4137 view.update(cx, |view, cx| {
4138 view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
4139 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
4140 view.end_selection(cx);
4141
4142 view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
4143 view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
4144 view.end_selection(cx);
4145 assert_eq!(
4146 view.selected_display_ranges(cx),
4147 [
4148 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
4149 DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
4150 ]
4151 );
4152 });
4153
4154 view.update(cx, |view, cx| {
4155 view.cancel(&Cancel, cx);
4156 assert_eq!(
4157 view.selected_display_ranges(cx),
4158 [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
4159 );
4160 });
4161
4162 view.update(cx, |view, cx| {
4163 view.cancel(&Cancel, cx);
4164 assert_eq!(
4165 view.selected_display_ranges(cx),
4166 [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
4167 );
4168 });
4169 }
4170
4171 #[gpui::test]
4172 fn test_fold(cx: &mut gpui::MutableAppContext) {
4173 let buffer = MultiBuffer::build_simple(
4174 &"
4175 impl Foo {
4176 // Hello!
4177
4178 fn a() {
4179 1
4180 }
4181
4182 fn b() {
4183 2
4184 }
4185
4186 fn c() {
4187 3
4188 }
4189 }
4190 "
4191 .unindent(),
4192 cx,
4193 );
4194 let settings = EditorSettings::test(&cx);
4195 let (_, view) = cx.add_window(Default::default(), |cx| {
4196 build_editor(buffer.clone(), settings, cx)
4197 });
4198
4199 view.update(cx, |view, cx| {
4200 view.select_display_ranges(&[DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)], cx);
4201 view.fold(&Fold, cx);
4202 assert_eq!(
4203 view.display_text(cx),
4204 "
4205 impl Foo {
4206 // Hello!
4207
4208 fn a() {
4209 1
4210 }
4211
4212 fn b() {…
4213 }
4214
4215 fn c() {…
4216 }
4217 }
4218 "
4219 .unindent(),
4220 );
4221
4222 view.fold(&Fold, cx);
4223 assert_eq!(
4224 view.display_text(cx),
4225 "
4226 impl Foo {…
4227 }
4228 "
4229 .unindent(),
4230 );
4231
4232 view.unfold(&Unfold, cx);
4233 assert_eq!(
4234 view.display_text(cx),
4235 "
4236 impl Foo {
4237 // Hello!
4238
4239 fn a() {
4240 1
4241 }
4242
4243 fn b() {…
4244 }
4245
4246 fn c() {…
4247 }
4248 }
4249 "
4250 .unindent(),
4251 );
4252
4253 view.unfold(&Unfold, cx);
4254 assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
4255 });
4256 }
4257
4258 #[gpui::test]
4259 fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
4260 let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
4261 let settings = EditorSettings::test(&cx);
4262 let (_, view) = cx.add_window(Default::default(), |cx| {
4263 build_editor(buffer.clone(), settings, cx)
4264 });
4265
4266 buffer.update(cx, |buffer, cx| {
4267 buffer.edit(
4268 vec![
4269 Point::new(1, 0)..Point::new(1, 0),
4270 Point::new(1, 1)..Point::new(1, 1),
4271 ],
4272 "\t",
4273 cx,
4274 );
4275 });
4276
4277 view.update(cx, |view, cx| {
4278 assert_eq!(
4279 view.selected_display_ranges(cx),
4280 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
4281 );
4282
4283 view.move_down(&MoveDown, cx);
4284 assert_eq!(
4285 view.selected_display_ranges(cx),
4286 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
4287 );
4288
4289 view.move_right(&MoveRight, cx);
4290 assert_eq!(
4291 view.selected_display_ranges(cx),
4292 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
4293 );
4294
4295 view.move_left(&MoveLeft, cx);
4296 assert_eq!(
4297 view.selected_display_ranges(cx),
4298 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
4299 );
4300
4301 view.move_up(&MoveUp, cx);
4302 assert_eq!(
4303 view.selected_display_ranges(cx),
4304 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
4305 );
4306
4307 view.move_to_end(&MoveToEnd, cx);
4308 assert_eq!(
4309 view.selected_display_ranges(cx),
4310 &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
4311 );
4312
4313 view.move_to_beginning(&MoveToBeginning, cx);
4314 assert_eq!(
4315 view.selected_display_ranges(cx),
4316 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
4317 );
4318
4319 view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)], cx);
4320 view.select_to_beginning(&SelectToBeginning, cx);
4321 assert_eq!(
4322 view.selected_display_ranges(cx),
4323 &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
4324 );
4325
4326 view.select_to_end(&SelectToEnd, cx);
4327 assert_eq!(
4328 view.selected_display_ranges(cx),
4329 &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
4330 );
4331 });
4332 }
4333
4334 #[gpui::test]
4335 fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
4336 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
4337 let settings = EditorSettings::test(&cx);
4338 let (_, view) = cx.add_window(Default::default(), |cx| {
4339 build_editor(buffer.clone(), settings, cx)
4340 });
4341
4342 assert_eq!('ⓐ'.len_utf8(), 3);
4343 assert_eq!('α'.len_utf8(), 2);
4344
4345 view.update(cx, |view, cx| {
4346 view.fold_ranges(
4347 vec![
4348 Point::new(0, 6)..Point::new(0, 12),
4349 Point::new(1, 2)..Point::new(1, 4),
4350 Point::new(2, 4)..Point::new(2, 8),
4351 ],
4352 cx,
4353 );
4354 assert_eq!(view.display_text(cx), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\n");
4355
4356 view.move_right(&MoveRight, cx);
4357 assert_eq!(
4358 view.selected_display_ranges(cx),
4359 &[empty_range(0, "ⓐ".len())]
4360 );
4361 view.move_right(&MoveRight, cx);
4362 assert_eq!(
4363 view.selected_display_ranges(cx),
4364 &[empty_range(0, "ⓐⓑ".len())]
4365 );
4366 view.move_right(&MoveRight, cx);
4367 assert_eq!(
4368 view.selected_display_ranges(cx),
4369 &[empty_range(0, "ⓐⓑ…".len())]
4370 );
4371
4372 view.move_down(&MoveDown, cx);
4373 assert_eq!(
4374 view.selected_display_ranges(cx),
4375 &[empty_range(1, "ab…".len())]
4376 );
4377 view.move_left(&MoveLeft, cx);
4378 assert_eq!(
4379 view.selected_display_ranges(cx),
4380 &[empty_range(1, "ab".len())]
4381 );
4382 view.move_left(&MoveLeft, cx);
4383 assert_eq!(
4384 view.selected_display_ranges(cx),
4385 &[empty_range(1, "a".len())]
4386 );
4387
4388 view.move_down(&MoveDown, cx);
4389 assert_eq!(
4390 view.selected_display_ranges(cx),
4391 &[empty_range(2, "α".len())]
4392 );
4393 view.move_right(&MoveRight, cx);
4394 assert_eq!(
4395 view.selected_display_ranges(cx),
4396 &[empty_range(2, "αβ".len())]
4397 );
4398 view.move_right(&MoveRight, cx);
4399 assert_eq!(
4400 view.selected_display_ranges(cx),
4401 &[empty_range(2, "αβ…".len())]
4402 );
4403 view.move_right(&MoveRight, cx);
4404 assert_eq!(
4405 view.selected_display_ranges(cx),
4406 &[empty_range(2, "αβ…ε".len())]
4407 );
4408
4409 view.move_up(&MoveUp, cx);
4410 assert_eq!(
4411 view.selected_display_ranges(cx),
4412 &[empty_range(1, "ab…e".len())]
4413 );
4414 view.move_up(&MoveUp, cx);
4415 assert_eq!(
4416 view.selected_display_ranges(cx),
4417 &[empty_range(0, "ⓐⓑ…ⓔ".len())]
4418 );
4419 view.move_left(&MoveLeft, cx);
4420 assert_eq!(
4421 view.selected_display_ranges(cx),
4422 &[empty_range(0, "ⓐⓑ…".len())]
4423 );
4424 view.move_left(&MoveLeft, cx);
4425 assert_eq!(
4426 view.selected_display_ranges(cx),
4427 &[empty_range(0, "ⓐⓑ".len())]
4428 );
4429 view.move_left(&MoveLeft, cx);
4430 assert_eq!(
4431 view.selected_display_ranges(cx),
4432 &[empty_range(0, "ⓐ".len())]
4433 );
4434 });
4435 }
4436
4437 #[gpui::test]
4438 fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
4439 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
4440 let settings = EditorSettings::test(&cx);
4441 let (_, view) = cx.add_window(Default::default(), |cx| {
4442 build_editor(buffer.clone(), settings, cx)
4443 });
4444 view.update(cx, |view, cx| {
4445 view.select_display_ranges(&[empty_range(0, "ⓐⓑⓒⓓⓔ".len())], cx);
4446 view.move_down(&MoveDown, cx);
4447 assert_eq!(
4448 view.selected_display_ranges(cx),
4449 &[empty_range(1, "abcd".len())]
4450 );
4451
4452 view.move_down(&MoveDown, cx);
4453 assert_eq!(
4454 view.selected_display_ranges(cx),
4455 &[empty_range(2, "αβγ".len())]
4456 );
4457
4458 view.move_down(&MoveDown, cx);
4459 assert_eq!(
4460 view.selected_display_ranges(cx),
4461 &[empty_range(3, "abcd".len())]
4462 );
4463
4464 view.move_down(&MoveDown, cx);
4465 assert_eq!(
4466 view.selected_display_ranges(cx),
4467 &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
4468 );
4469
4470 view.move_up(&MoveUp, cx);
4471 assert_eq!(
4472 view.selected_display_ranges(cx),
4473 &[empty_range(3, "abcd".len())]
4474 );
4475
4476 view.move_up(&MoveUp, cx);
4477 assert_eq!(
4478 view.selected_display_ranges(cx),
4479 &[empty_range(2, "αβγ".len())]
4480 );
4481 });
4482 }
4483
4484 #[gpui::test]
4485 fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
4486 let buffer = MultiBuffer::build_simple("abc\n def", cx);
4487 let settings = EditorSettings::test(&cx);
4488 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
4489 view.update(cx, |view, cx| {
4490 view.select_display_ranges(
4491 &[
4492 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
4493 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
4494 ],
4495 cx,
4496 );
4497 });
4498
4499 view.update(cx, |view, cx| {
4500 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
4501 assert_eq!(
4502 view.selected_display_ranges(cx),
4503 &[
4504 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
4505 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
4506 ]
4507 );
4508 });
4509
4510 view.update(cx, |view, cx| {
4511 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
4512 assert_eq!(
4513 view.selected_display_ranges(cx),
4514 &[
4515 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
4516 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
4517 ]
4518 );
4519 });
4520
4521 view.update(cx, |view, cx| {
4522 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
4523 assert_eq!(
4524 view.selected_display_ranges(cx),
4525 &[
4526 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
4527 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
4528 ]
4529 );
4530 });
4531
4532 view.update(cx, |view, cx| {
4533 view.move_to_end_of_line(&MoveToEndOfLine, cx);
4534 assert_eq!(
4535 view.selected_display_ranges(cx),
4536 &[
4537 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
4538 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
4539 ]
4540 );
4541 });
4542
4543 // Moving to the end of line again is a no-op.
4544 view.update(cx, |view, cx| {
4545 view.move_to_end_of_line(&MoveToEndOfLine, cx);
4546 assert_eq!(
4547 view.selected_display_ranges(cx),
4548 &[
4549 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
4550 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
4551 ]
4552 );
4553 });
4554
4555 view.update(cx, |view, cx| {
4556 view.move_left(&MoveLeft, cx);
4557 view.select_to_beginning_of_line(&SelectToBeginningOfLine(true), cx);
4558 assert_eq!(
4559 view.selected_display_ranges(cx),
4560 &[
4561 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
4562 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
4563 ]
4564 );
4565 });
4566
4567 view.update(cx, |view, cx| {
4568 view.select_to_beginning_of_line(&SelectToBeginningOfLine(true), cx);
4569 assert_eq!(
4570 view.selected_display_ranges(cx),
4571 &[
4572 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
4573 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
4574 ]
4575 );
4576 });
4577
4578 view.update(cx, |view, cx| {
4579 view.select_to_beginning_of_line(&SelectToBeginningOfLine(true), cx);
4580 assert_eq!(
4581 view.selected_display_ranges(cx),
4582 &[
4583 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
4584 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
4585 ]
4586 );
4587 });
4588
4589 view.update(cx, |view, cx| {
4590 view.select_to_end_of_line(&SelectToEndOfLine, cx);
4591 assert_eq!(
4592 view.selected_display_ranges(cx),
4593 &[
4594 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
4595 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
4596 ]
4597 );
4598 });
4599
4600 view.update(cx, |view, cx| {
4601 view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
4602 assert_eq!(view.display_text(cx), "ab\n de");
4603 assert_eq!(
4604 view.selected_display_ranges(cx),
4605 &[
4606 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
4607 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
4608 ]
4609 );
4610 });
4611
4612 view.update(cx, |view, cx| {
4613 view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
4614 assert_eq!(view.display_text(cx), "\n");
4615 assert_eq!(
4616 view.selected_display_ranges(cx),
4617 &[
4618 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
4619 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
4620 ]
4621 );
4622 });
4623 }
4624
4625 #[gpui::test]
4626 fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
4627 let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
4628 let settings = EditorSettings::test(&cx);
4629 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
4630 view.update(cx, |view, cx| {
4631 view.select_display_ranges(
4632 &[
4633 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
4634 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
4635 ],
4636 cx,
4637 );
4638 });
4639
4640 view.update(cx, |view, cx| {
4641 view.move_to_previous_word_boundary(&MoveToPreviousWordBoundary, cx);
4642 assert_eq!(
4643 view.selected_display_ranges(cx),
4644 &[
4645 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 9),
4646 DisplayPoint::new(2, 3)..DisplayPoint::new(2, 3),
4647 ]
4648 );
4649 });
4650
4651 view.update(cx, |view, cx| {
4652 view.move_to_previous_word_boundary(&MoveToPreviousWordBoundary, cx);
4653 assert_eq!(
4654 view.selected_display_ranges(cx),
4655 &[
4656 DisplayPoint::new(0, 7)..DisplayPoint::new(0, 7),
4657 DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2),
4658 ]
4659 );
4660 });
4661
4662 view.update(cx, |view, cx| {
4663 view.move_to_previous_word_boundary(&MoveToPreviousWordBoundary, cx);
4664 assert_eq!(
4665 view.selected_display_ranges(cx),
4666 &[
4667 DisplayPoint::new(0, 4)..DisplayPoint::new(0, 4),
4668 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
4669 ]
4670 );
4671 });
4672
4673 view.update(cx, |view, cx| {
4674 view.move_to_previous_word_boundary(&MoveToPreviousWordBoundary, cx);
4675 assert_eq!(
4676 view.selected_display_ranges(cx),
4677 &[
4678 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
4679 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
4680 ]
4681 );
4682 });
4683
4684 view.update(cx, |view, cx| {
4685 view.move_to_previous_word_boundary(&MoveToPreviousWordBoundary, cx);
4686 assert_eq!(
4687 view.selected_display_ranges(cx),
4688 &[
4689 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
4690 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 23),
4691 ]
4692 );
4693 });
4694
4695 view.update(cx, |view, cx| {
4696 view.move_to_next_word_boundary(&MoveToNextWordBoundary, cx);
4697 assert_eq!(
4698 view.selected_display_ranges(cx),
4699 &[
4700 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
4701 DisplayPoint::new(0, 24)..DisplayPoint::new(0, 24),
4702 ]
4703 );
4704 });
4705
4706 view.update(cx, |view, cx| {
4707 view.move_to_next_word_boundary(&MoveToNextWordBoundary, cx);
4708 assert_eq!(
4709 view.selected_display_ranges(cx),
4710 &[
4711 DisplayPoint::new(0, 7)..DisplayPoint::new(0, 7),
4712 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
4713 ]
4714 );
4715 });
4716
4717 view.update(cx, |view, cx| {
4718 view.move_to_next_word_boundary(&MoveToNextWordBoundary, cx);
4719 assert_eq!(
4720 view.selected_display_ranges(cx),
4721 &[
4722 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 9),
4723 DisplayPoint::new(2, 3)..DisplayPoint::new(2, 3),
4724 ]
4725 );
4726 });
4727
4728 view.update(cx, |view, cx| {
4729 view.move_right(&MoveRight, cx);
4730 view.select_to_previous_word_boundary(&SelectToPreviousWordBoundary, cx);
4731 assert_eq!(
4732 view.selected_display_ranges(cx),
4733 &[
4734 DisplayPoint::new(0, 10)..DisplayPoint::new(0, 9),
4735 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 3),
4736 ]
4737 );
4738 });
4739
4740 view.update(cx, |view, cx| {
4741 view.select_to_previous_word_boundary(&SelectToPreviousWordBoundary, cx);
4742 assert_eq!(
4743 view.selected_display_ranges(cx),
4744 &[
4745 DisplayPoint::new(0, 10)..DisplayPoint::new(0, 7),
4746 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 2),
4747 ]
4748 );
4749 });
4750
4751 view.update(cx, |view, cx| {
4752 view.select_to_next_word_boundary(&SelectToNextWordBoundary, cx);
4753 assert_eq!(
4754 view.selected_display_ranges(cx),
4755 &[
4756 DisplayPoint::new(0, 10)..DisplayPoint::new(0, 9),
4757 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 3),
4758 ]
4759 );
4760 });
4761 }
4762
4763 #[gpui::test]
4764 fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
4765 let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
4766 let settings = EditorSettings::test(&cx);
4767 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
4768
4769 view.update(cx, |view, cx| {
4770 view.set_wrap_width(Some(140.), cx);
4771 assert_eq!(
4772 view.display_text(cx),
4773 "use one::{\n two::three::\n four::five\n};"
4774 );
4775
4776 view.select_display_ranges(&[DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)], cx);
4777
4778 view.move_to_next_word_boundary(&MoveToNextWordBoundary, cx);
4779 assert_eq!(
4780 view.selected_display_ranges(cx),
4781 &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
4782 );
4783
4784 view.move_to_next_word_boundary(&MoveToNextWordBoundary, cx);
4785 assert_eq!(
4786 view.selected_display_ranges(cx),
4787 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
4788 );
4789
4790 view.move_to_next_word_boundary(&MoveToNextWordBoundary, cx);
4791 assert_eq!(
4792 view.selected_display_ranges(cx),
4793 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
4794 );
4795
4796 view.move_to_next_word_boundary(&MoveToNextWordBoundary, cx);
4797 assert_eq!(
4798 view.selected_display_ranges(cx),
4799 &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
4800 );
4801
4802 view.move_to_previous_word_boundary(&MoveToPreviousWordBoundary, cx);
4803 assert_eq!(
4804 view.selected_display_ranges(cx),
4805 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
4806 );
4807
4808 view.move_to_previous_word_boundary(&MoveToPreviousWordBoundary, cx);
4809 assert_eq!(
4810 view.selected_display_ranges(cx),
4811 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
4812 );
4813 });
4814 }
4815
4816 #[gpui::test]
4817 fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
4818 let buffer = MultiBuffer::build_simple("one two three four", cx);
4819 let settings = EditorSettings::test(&cx);
4820 let (_, view) = cx.add_window(Default::default(), |cx| {
4821 build_editor(buffer.clone(), settings, cx)
4822 });
4823
4824 view.update(cx, |view, cx| {
4825 view.select_display_ranges(
4826 &[
4827 // an empty selection - the preceding word fragment is deleted
4828 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
4829 // characters selected - they are deleted
4830 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
4831 ],
4832 cx,
4833 );
4834 view.delete_to_previous_word_boundary(&DeleteToPreviousWordBoundary, cx);
4835 });
4836
4837 assert_eq!(buffer.read(cx).read(cx).text(), "e two te four");
4838
4839 view.update(cx, |view, cx| {
4840 view.select_display_ranges(
4841 &[
4842 // an empty selection - the following word fragment is deleted
4843 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
4844 // characters selected - they are deleted
4845 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
4846 ],
4847 cx,
4848 );
4849 view.delete_to_next_word_boundary(&DeleteToNextWordBoundary, cx);
4850 });
4851
4852 assert_eq!(buffer.read(cx).read(cx).text(), "e t te our");
4853 }
4854
4855 #[gpui::test]
4856 fn test_newline(cx: &mut gpui::MutableAppContext) {
4857 let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
4858 let settings = EditorSettings::test(&cx);
4859 let (_, view) = cx.add_window(Default::default(), |cx| {
4860 build_editor(buffer.clone(), settings, cx)
4861 });
4862
4863 view.update(cx, |view, cx| {
4864 view.select_display_ranges(
4865 &[
4866 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
4867 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
4868 DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
4869 ],
4870 cx,
4871 );
4872
4873 view.newline(&Newline, cx);
4874 assert_eq!(view.text(cx), "aa\naa\n \n bb\n bb\n");
4875 });
4876 }
4877
4878 #[gpui::test]
4879 fn test_indent_outdent(cx: &mut gpui::MutableAppContext) {
4880 let buffer = MultiBuffer::build_simple(" one two\nthree\n four", cx);
4881 let settings = EditorSettings::test(&cx);
4882 let (_, view) = cx.add_window(Default::default(), |cx| {
4883 build_editor(buffer.clone(), settings, cx)
4884 });
4885
4886 view.update(cx, |view, cx| {
4887 // two selections on the same line
4888 view.select_display_ranges(
4889 &[
4890 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 5),
4891 DisplayPoint::new(0, 6)..DisplayPoint::new(0, 9),
4892 ],
4893 cx,
4894 );
4895
4896 // indent from mid-tabstop to full tabstop
4897 view.tab(&Tab, cx);
4898 assert_eq!(view.text(cx), " one two\nthree\n four");
4899 assert_eq!(
4900 view.selected_display_ranges(cx),
4901 &[
4902 DisplayPoint::new(0, 4)..DisplayPoint::new(0, 7),
4903 DisplayPoint::new(0, 8)..DisplayPoint::new(0, 11),
4904 ]
4905 );
4906
4907 // outdent from 1 tabstop to 0 tabstops
4908 view.outdent(&Outdent, cx);
4909 assert_eq!(view.text(cx), "one two\nthree\n four");
4910 assert_eq!(
4911 view.selected_display_ranges(cx),
4912 &[
4913 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 3),
4914 DisplayPoint::new(0, 4)..DisplayPoint::new(0, 7),
4915 ]
4916 );
4917
4918 // select across line ending
4919 view.select_display_ranges(&[DisplayPoint::new(1, 1)..DisplayPoint::new(2, 0)], cx);
4920
4921 // indent and outdent affect only the preceding line
4922 view.tab(&Tab, cx);
4923 assert_eq!(view.text(cx), "one two\n three\n four");
4924 assert_eq!(
4925 view.selected_display_ranges(cx),
4926 &[DisplayPoint::new(1, 5)..DisplayPoint::new(2, 0)]
4927 );
4928 view.outdent(&Outdent, cx);
4929 assert_eq!(view.text(cx), "one two\nthree\n four");
4930 assert_eq!(
4931 view.selected_display_ranges(cx),
4932 &[DisplayPoint::new(1, 1)..DisplayPoint::new(2, 0)]
4933 );
4934
4935 // Ensure that indenting/outdenting works when the cursor is at column 0.
4936 view.select_display_ranges(&[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)], cx);
4937 view.tab(&Tab, cx);
4938 assert_eq!(view.text(cx), "one two\n three\n four");
4939 assert_eq!(
4940 view.selected_display_ranges(cx),
4941 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
4942 );
4943
4944 view.select_display_ranges(&[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)], cx);
4945 view.outdent(&Outdent, cx);
4946 assert_eq!(view.text(cx), "one two\nthree\n four");
4947 assert_eq!(
4948 view.selected_display_ranges(cx),
4949 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
4950 );
4951 });
4952 }
4953
4954 #[gpui::test]
4955 fn test_backspace(cx: &mut gpui::MutableAppContext) {
4956 let buffer =
4957 MultiBuffer::build_simple("one two three\nfour five six\nseven eight nine\nten\n", cx);
4958 let settings = EditorSettings::test(&cx);
4959 let (_, view) = cx.add_window(Default::default(), |cx| {
4960 build_editor(buffer.clone(), settings, cx)
4961 });
4962
4963 view.update(cx, |view, cx| {
4964 view.select_display_ranges(
4965 &[
4966 // an empty selection - the preceding character is deleted
4967 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
4968 // one character selected - it is deleted
4969 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
4970 // a line suffix selected - it is deleted
4971 DisplayPoint::new(2, 6)..DisplayPoint::new(3, 0),
4972 ],
4973 cx,
4974 );
4975 view.backspace(&Backspace, cx);
4976 });
4977
4978 assert_eq!(
4979 buffer.read(cx).read(cx).text(),
4980 "oe two three\nfou five six\nseven ten\n"
4981 );
4982 }
4983
4984 #[gpui::test]
4985 fn test_delete(cx: &mut gpui::MutableAppContext) {
4986 let buffer =
4987 MultiBuffer::build_simple("one two three\nfour five six\nseven eight nine\nten\n", cx);
4988 let settings = EditorSettings::test(&cx);
4989 let (_, view) = cx.add_window(Default::default(), |cx| {
4990 build_editor(buffer.clone(), settings, cx)
4991 });
4992
4993 view.update(cx, |view, cx| {
4994 view.select_display_ranges(
4995 &[
4996 // an empty selection - the following character is deleted
4997 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
4998 // one character selected - it is deleted
4999 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
5000 // a line suffix selected - it is deleted
5001 DisplayPoint::new(2, 6)..DisplayPoint::new(3, 0),
5002 ],
5003 cx,
5004 );
5005 view.delete(&Delete, cx);
5006 });
5007
5008 assert_eq!(
5009 buffer.read(cx).read(cx).text(),
5010 "on two three\nfou five six\nseven ten\n"
5011 );
5012 }
5013
5014 #[gpui::test]
5015 fn test_delete_line(cx: &mut gpui::MutableAppContext) {
5016 let settings = EditorSettings::test(&cx);
5017 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
5018 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
5019 view.update(cx, |view, cx| {
5020 view.select_display_ranges(
5021 &[
5022 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
5023 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
5024 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
5025 ],
5026 cx,
5027 );
5028 view.delete_line(&DeleteLine, cx);
5029 assert_eq!(view.display_text(cx), "ghi");
5030 assert_eq!(
5031 view.selected_display_ranges(cx),
5032 vec![
5033 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
5034 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
5035 ]
5036 );
5037 });
5038
5039 let settings = EditorSettings::test(&cx);
5040 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
5041 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
5042 view.update(cx, |view, cx| {
5043 view.select_display_ranges(&[DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)], cx);
5044 view.delete_line(&DeleteLine, cx);
5045 assert_eq!(view.display_text(cx), "ghi\n");
5046 assert_eq!(
5047 view.selected_display_ranges(cx),
5048 vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
5049 );
5050 });
5051 }
5052
5053 #[gpui::test]
5054 fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
5055 let settings = EditorSettings::test(&cx);
5056 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
5057 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
5058 view.update(cx, |view, cx| {
5059 view.select_display_ranges(
5060 &[
5061 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
5062 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
5063 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
5064 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
5065 ],
5066 cx,
5067 );
5068 view.duplicate_line(&DuplicateLine, cx);
5069 assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
5070 assert_eq!(
5071 view.selected_display_ranges(cx),
5072 vec![
5073 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
5074 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
5075 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
5076 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
5077 ]
5078 );
5079 });
5080
5081 let settings = EditorSettings::test(&cx);
5082 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
5083 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
5084 view.update(cx, |view, cx| {
5085 view.select_display_ranges(
5086 &[
5087 DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
5088 DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
5089 ],
5090 cx,
5091 );
5092 view.duplicate_line(&DuplicateLine, cx);
5093 assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
5094 assert_eq!(
5095 view.selected_display_ranges(cx),
5096 vec![
5097 DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
5098 DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
5099 ]
5100 );
5101 });
5102 }
5103
5104 #[gpui::test]
5105 fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
5106 let settings = EditorSettings::test(&cx);
5107 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
5108 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
5109 view.update(cx, |view, cx| {
5110 view.fold_ranges(
5111 vec![
5112 Point::new(0, 2)..Point::new(1, 2),
5113 Point::new(2, 3)..Point::new(4, 1),
5114 Point::new(7, 0)..Point::new(8, 4),
5115 ],
5116 cx,
5117 );
5118 view.select_display_ranges(
5119 &[
5120 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
5121 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
5122 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
5123 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
5124 ],
5125 cx,
5126 );
5127 assert_eq!(
5128 view.display_text(cx),
5129 "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
5130 );
5131
5132 view.move_line_up(&MoveLineUp, cx);
5133 assert_eq!(
5134 view.display_text(cx),
5135 "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
5136 );
5137 assert_eq!(
5138 view.selected_display_ranges(cx),
5139 vec![
5140 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
5141 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
5142 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
5143 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
5144 ]
5145 );
5146 });
5147
5148 view.update(cx, |view, cx| {
5149 view.move_line_down(&MoveLineDown, cx);
5150 assert_eq!(
5151 view.display_text(cx),
5152 "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
5153 );
5154 assert_eq!(
5155 view.selected_display_ranges(cx),
5156 vec![
5157 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
5158 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
5159 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
5160 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
5161 ]
5162 );
5163 });
5164
5165 view.update(cx, |view, cx| {
5166 view.move_line_down(&MoveLineDown, cx);
5167 assert_eq!(
5168 view.display_text(cx),
5169 "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
5170 );
5171 assert_eq!(
5172 view.selected_display_ranges(cx),
5173 vec![
5174 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
5175 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
5176 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
5177 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
5178 ]
5179 );
5180 });
5181
5182 view.update(cx, |view, cx| {
5183 view.move_line_up(&MoveLineUp, cx);
5184 assert_eq!(
5185 view.display_text(cx),
5186 "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
5187 );
5188 assert_eq!(
5189 view.selected_display_ranges(cx),
5190 vec![
5191 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
5192 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
5193 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
5194 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
5195 ]
5196 );
5197 });
5198 }
5199
5200 #[gpui::test]
5201 fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
5202 let settings = EditorSettings::test(&cx);
5203 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
5204 let snapshot = buffer.read(cx).snapshot(cx);
5205 let (_, editor) =
5206 cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
5207 editor.update(cx, |editor, cx| {
5208 editor.insert_blocks(
5209 [BlockProperties {
5210 position: snapshot.anchor_after(Point::new(2, 0)),
5211 disposition: BlockDisposition::Below,
5212 height: 1,
5213 render: Arc::new(|_| Empty::new().boxed()),
5214 }],
5215 cx,
5216 );
5217 editor.select_ranges([Point::new(2, 0)..Point::new(2, 0)], None, cx);
5218 editor.move_line_down(&MoveLineDown, cx);
5219 });
5220 }
5221
5222 #[gpui::test]
5223 fn test_clipboard(cx: &mut gpui::MutableAppContext) {
5224 let buffer = MultiBuffer::build_simple("one✅ two three four five six ", cx);
5225 let settings = EditorSettings::test(&cx);
5226 let view = cx
5227 .add_window(Default::default(), |cx| {
5228 build_editor(buffer.clone(), settings, cx)
5229 })
5230 .1;
5231
5232 // Cut with three selections. Clipboard text is divided into three slices.
5233 view.update(cx, |view, cx| {
5234 view.select_ranges(vec![0..7, 11..17, 22..27], None, cx);
5235 view.cut(&Cut, cx);
5236 assert_eq!(view.display_text(cx), "two four six ");
5237 });
5238
5239 // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
5240 view.update(cx, |view, cx| {
5241 view.select_ranges(vec![4..4, 9..9, 13..13], None, cx);
5242 view.paste(&Paste, cx);
5243 assert_eq!(view.display_text(cx), "two one✅ four three six five ");
5244 assert_eq!(
5245 view.selected_display_ranges(cx),
5246 &[
5247 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
5248 DisplayPoint::new(0, 22)..DisplayPoint::new(0, 22),
5249 DisplayPoint::new(0, 31)..DisplayPoint::new(0, 31)
5250 ]
5251 );
5252 });
5253
5254 // Paste again but with only two cursors. Since the number of cursors doesn't
5255 // match the number of slices in the clipboard, the entire clipboard text
5256 // is pasted at each cursor.
5257 view.update(cx, |view, cx| {
5258 view.select_ranges(vec![0..0, 31..31], None, cx);
5259 view.handle_input(&Input("( ".into()), cx);
5260 view.paste(&Paste, cx);
5261 view.handle_input(&Input(") ".into()), cx);
5262 assert_eq!(
5263 view.display_text(cx),
5264 "( one✅ three five ) two one✅ four three six five ( one✅ three five ) "
5265 );
5266 });
5267
5268 view.update(cx, |view, cx| {
5269 view.select_ranges(vec![0..0], None, cx);
5270 view.handle_input(&Input("123\n4567\n89\n".into()), cx);
5271 assert_eq!(
5272 view.display_text(cx),
5273 "123\n4567\n89\n( one✅ three five ) two one✅ four three six five ( one✅ three five ) "
5274 );
5275 });
5276
5277 // Cut with three selections, one of which is full-line.
5278 view.update(cx, |view, cx| {
5279 view.select_display_ranges(
5280 &[
5281 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2),
5282 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
5283 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
5284 ],
5285 cx,
5286 );
5287 view.cut(&Cut, cx);
5288 assert_eq!(
5289 view.display_text(cx),
5290 "13\n9\n( one✅ three five ) two one✅ four three six five ( one✅ three five ) "
5291 );
5292 });
5293
5294 // Paste with three selections, noticing how the copied selection that was full-line
5295 // gets inserted before the second cursor.
5296 view.update(cx, |view, cx| {
5297 view.select_display_ranges(
5298 &[
5299 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
5300 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
5301 DisplayPoint::new(2, 2)..DisplayPoint::new(2, 3),
5302 ],
5303 cx,
5304 );
5305 view.paste(&Paste, cx);
5306 assert_eq!(
5307 view.display_text(cx),
5308 "123\n4567\n9\n( 8ne✅ three five ) two one✅ four three six five ( one✅ three five ) "
5309 );
5310 assert_eq!(
5311 view.selected_display_ranges(cx),
5312 &[
5313 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
5314 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
5315 DisplayPoint::new(3, 3)..DisplayPoint::new(3, 3),
5316 ]
5317 );
5318 });
5319
5320 // Copy with a single cursor only, which writes the whole line into the clipboard.
5321 view.update(cx, |view, cx| {
5322 view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)], cx);
5323 view.copy(&Copy, cx);
5324 });
5325
5326 // Paste with three selections, noticing how the copied full-line selection is inserted
5327 // before the empty selections but replaces the selection that is non-empty.
5328 view.update(cx, |view, cx| {
5329 view.select_display_ranges(
5330 &[
5331 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
5332 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 2),
5333 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
5334 ],
5335 cx,
5336 );
5337 view.paste(&Paste, cx);
5338 assert_eq!(
5339 view.display_text(cx),
5340 "123\n123\n123\n67\n123\n9\n( 8ne✅ three five ) two one✅ four three six five ( one✅ three five ) "
5341 );
5342 assert_eq!(
5343 view.selected_display_ranges(cx),
5344 &[
5345 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
5346 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
5347 DisplayPoint::new(5, 1)..DisplayPoint::new(5, 1),
5348 ]
5349 );
5350 });
5351 }
5352
5353 #[gpui::test]
5354 fn test_select_all(cx: &mut gpui::MutableAppContext) {
5355 let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
5356 let settings = EditorSettings::test(&cx);
5357 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
5358 view.update(cx, |view, cx| {
5359 view.select_all(&SelectAll, cx);
5360 assert_eq!(
5361 view.selected_display_ranges(cx),
5362 &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
5363 );
5364 });
5365 }
5366
5367 #[gpui::test]
5368 fn test_select_line(cx: &mut gpui::MutableAppContext) {
5369 let settings = EditorSettings::test(&cx);
5370 let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
5371 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
5372 view.update(cx, |view, cx| {
5373 view.select_display_ranges(
5374 &[
5375 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
5376 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
5377 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
5378 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
5379 ],
5380 cx,
5381 );
5382 view.select_line(&SelectLine, cx);
5383 assert_eq!(
5384 view.selected_display_ranges(cx),
5385 vec![
5386 DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
5387 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
5388 ]
5389 );
5390 });
5391
5392 view.update(cx, |view, cx| {
5393 view.select_line(&SelectLine, cx);
5394 assert_eq!(
5395 view.selected_display_ranges(cx),
5396 vec![
5397 DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
5398 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
5399 ]
5400 );
5401 });
5402
5403 view.update(cx, |view, cx| {
5404 view.select_line(&SelectLine, cx);
5405 assert_eq!(
5406 view.selected_display_ranges(cx),
5407 vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
5408 );
5409 });
5410 }
5411
5412 #[gpui::test]
5413 fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
5414 let settings = EditorSettings::test(&cx);
5415 let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
5416 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
5417 view.update(cx, |view, cx| {
5418 view.fold_ranges(
5419 vec![
5420 Point::new(0, 2)..Point::new(1, 2),
5421 Point::new(2, 3)..Point::new(4, 1),
5422 Point::new(7, 0)..Point::new(8, 4),
5423 ],
5424 cx,
5425 );
5426 view.select_display_ranges(
5427 &[
5428 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
5429 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
5430 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
5431 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
5432 ],
5433 cx,
5434 );
5435 assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i");
5436 });
5437
5438 view.update(cx, |view, cx| {
5439 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
5440 assert_eq!(
5441 view.display_text(cx),
5442 "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i"
5443 );
5444 assert_eq!(
5445 view.selected_display_ranges(cx),
5446 [
5447 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
5448 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
5449 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
5450 DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
5451 ]
5452 );
5453 });
5454
5455 view.update(cx, |view, cx| {
5456 view.select_display_ranges(&[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)], cx);
5457 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
5458 assert_eq!(
5459 view.display_text(cx),
5460 "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
5461 );
5462 assert_eq!(
5463 view.selected_display_ranges(cx),
5464 [
5465 DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
5466 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
5467 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
5468 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
5469 DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
5470 DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
5471 DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
5472 DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
5473 ]
5474 );
5475 });
5476 }
5477
5478 #[gpui::test]
5479 fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
5480 let settings = EditorSettings::test(&cx);
5481 let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
5482 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
5483
5484 view.update(cx, |view, cx| {
5485 view.select_display_ranges(&[DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)], cx);
5486 });
5487 view.update(cx, |view, cx| {
5488 view.add_selection_above(&AddSelectionAbove, cx);
5489 assert_eq!(
5490 view.selected_display_ranges(cx),
5491 vec![
5492 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
5493 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
5494 ]
5495 );
5496 });
5497
5498 view.update(cx, |view, cx| {
5499 view.add_selection_above(&AddSelectionAbove, cx);
5500 assert_eq!(
5501 view.selected_display_ranges(cx),
5502 vec![
5503 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
5504 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
5505 ]
5506 );
5507 });
5508
5509 view.update(cx, |view, cx| {
5510 view.add_selection_below(&AddSelectionBelow, cx);
5511 assert_eq!(
5512 view.selected_display_ranges(cx),
5513 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
5514 );
5515 });
5516
5517 view.update(cx, |view, cx| {
5518 view.add_selection_below(&AddSelectionBelow, cx);
5519 assert_eq!(
5520 view.selected_display_ranges(cx),
5521 vec![
5522 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
5523 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
5524 ]
5525 );
5526 });
5527
5528 view.update(cx, |view, cx| {
5529 view.add_selection_below(&AddSelectionBelow, cx);
5530 assert_eq!(
5531 view.selected_display_ranges(cx),
5532 vec![
5533 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
5534 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
5535 ]
5536 );
5537 });
5538
5539 view.update(cx, |view, cx| {
5540 view.select_display_ranges(&[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)], cx);
5541 });
5542 view.update(cx, |view, cx| {
5543 view.add_selection_below(&AddSelectionBelow, cx);
5544 assert_eq!(
5545 view.selected_display_ranges(cx),
5546 vec![
5547 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
5548 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
5549 ]
5550 );
5551 });
5552
5553 view.update(cx, |view, cx| {
5554 view.add_selection_below(&AddSelectionBelow, cx);
5555 assert_eq!(
5556 view.selected_display_ranges(cx),
5557 vec![
5558 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
5559 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
5560 ]
5561 );
5562 });
5563
5564 view.update(cx, |view, cx| {
5565 view.add_selection_above(&AddSelectionAbove, cx);
5566 assert_eq!(
5567 view.selected_display_ranges(cx),
5568 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
5569 );
5570 });
5571
5572 view.update(cx, |view, cx| {
5573 view.add_selection_above(&AddSelectionAbove, cx);
5574 assert_eq!(
5575 view.selected_display_ranges(cx),
5576 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
5577 );
5578 });
5579
5580 view.update(cx, |view, cx| {
5581 view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)], cx);
5582 view.add_selection_below(&AddSelectionBelow, cx);
5583 assert_eq!(
5584 view.selected_display_ranges(cx),
5585 vec![
5586 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
5587 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
5588 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
5589 ]
5590 );
5591 });
5592
5593 view.update(cx, |view, cx| {
5594 view.add_selection_below(&AddSelectionBelow, cx);
5595 assert_eq!(
5596 view.selected_display_ranges(cx),
5597 vec![
5598 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
5599 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
5600 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
5601 DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
5602 ]
5603 );
5604 });
5605
5606 view.update(cx, |view, cx| {
5607 view.add_selection_above(&AddSelectionAbove, cx);
5608 assert_eq!(
5609 view.selected_display_ranges(cx),
5610 vec![
5611 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
5612 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
5613 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
5614 ]
5615 );
5616 });
5617
5618 view.update(cx, |view, cx| {
5619 view.select_display_ranges(&[DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)], cx);
5620 });
5621 view.update(cx, |view, cx| {
5622 view.add_selection_above(&AddSelectionAbove, cx);
5623 assert_eq!(
5624 view.selected_display_ranges(cx),
5625 vec![
5626 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
5627 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
5628 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
5629 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
5630 ]
5631 );
5632 });
5633
5634 view.update(cx, |view, cx| {
5635 view.add_selection_below(&AddSelectionBelow, cx);
5636 assert_eq!(
5637 view.selected_display_ranges(cx),
5638 vec![
5639 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
5640 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
5641 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
5642 ]
5643 );
5644 });
5645 }
5646
5647 #[gpui::test]
5648 async fn test_select_larger_smaller_syntax_node(mut cx: gpui::TestAppContext) {
5649 let settings = cx.read(EditorSettings::test);
5650 let language = Some(Arc::new(Language::new(
5651 LanguageConfig::default(),
5652 Some(tree_sitter_rust::language()),
5653 )));
5654
5655 let text = r#"
5656 use mod1::mod2::{mod3, mod4};
5657
5658 fn fn_1(param1: bool, param2: &str) {
5659 let var1 = "text";
5660 }
5661 "#
5662 .unindent();
5663
5664 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, None, cx));
5665 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
5666 let (_, view) = cx.add_window(|cx| build_editor(buffer, settings, cx));
5667 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5668 .await;
5669
5670 view.update(&mut cx, |view, cx| {
5671 view.select_display_ranges(
5672 &[
5673 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
5674 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
5675 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
5676 ],
5677 cx,
5678 );
5679 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
5680 });
5681 assert_eq!(
5682 view.update(&mut cx, |view, cx| view.selected_display_ranges(cx)),
5683 &[
5684 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
5685 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
5686 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
5687 ]
5688 );
5689
5690 view.update(&mut cx, |view, cx| {
5691 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
5692 });
5693 assert_eq!(
5694 view.update(&mut cx, |view, cx| view.selected_display_ranges(cx)),
5695 &[
5696 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
5697 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
5698 ]
5699 );
5700
5701 view.update(&mut cx, |view, cx| {
5702 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
5703 });
5704 assert_eq!(
5705 view.update(&mut cx, |view, cx| view.selected_display_ranges(cx)),
5706 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
5707 );
5708
5709 // Trying to expand the selected syntax node one more time has no effect.
5710 view.update(&mut cx, |view, cx| {
5711 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
5712 });
5713 assert_eq!(
5714 view.update(&mut cx, |view, cx| view.selected_display_ranges(cx)),
5715 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
5716 );
5717
5718 view.update(&mut cx, |view, cx| {
5719 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
5720 });
5721 assert_eq!(
5722 view.update(&mut cx, |view, cx| view.selected_display_ranges(cx)),
5723 &[
5724 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
5725 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
5726 ]
5727 );
5728
5729 view.update(&mut cx, |view, cx| {
5730 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
5731 });
5732 assert_eq!(
5733 view.update(&mut cx, |view, cx| view.selected_display_ranges(cx)),
5734 &[
5735 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
5736 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
5737 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
5738 ]
5739 );
5740
5741 view.update(&mut cx, |view, cx| {
5742 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
5743 });
5744 assert_eq!(
5745 view.update(&mut cx, |view, cx| view.selected_display_ranges(cx)),
5746 &[
5747 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
5748 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
5749 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
5750 ]
5751 );
5752
5753 // Trying to shrink the selected syntax node one more time has no effect.
5754 view.update(&mut cx, |view, cx| {
5755 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
5756 });
5757 assert_eq!(
5758 view.update(&mut cx, |view, cx| view.selected_display_ranges(cx)),
5759 &[
5760 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
5761 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
5762 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
5763 ]
5764 );
5765
5766 // Ensure that we keep expanding the selection if the larger selection starts or ends within
5767 // a fold.
5768 view.update(&mut cx, |view, cx| {
5769 view.fold_ranges(
5770 vec![
5771 Point::new(0, 21)..Point::new(0, 24),
5772 Point::new(3, 20)..Point::new(3, 22),
5773 ],
5774 cx,
5775 );
5776 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
5777 });
5778 assert_eq!(
5779 view.update(&mut cx, |view, cx| view.selected_display_ranges(cx)),
5780 &[
5781 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
5782 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
5783 DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
5784 ]
5785 );
5786 }
5787
5788 #[gpui::test]
5789 async fn test_autoindent_selections(mut cx: gpui::TestAppContext) {
5790 let settings = cx.read(EditorSettings::test);
5791 let language = Some(Arc::new(
5792 Language::new(
5793 LanguageConfig {
5794 brackets: vec![
5795 BracketPair {
5796 start: "{".to_string(),
5797 end: "}".to_string(),
5798 close: false,
5799 newline: true,
5800 },
5801 BracketPair {
5802 start: "(".to_string(),
5803 end: ")".to_string(),
5804 close: false,
5805 newline: true,
5806 },
5807 ],
5808 ..Default::default()
5809 },
5810 Some(tree_sitter_rust::language()),
5811 )
5812 .with_indents_query(
5813 r#"
5814 (_ "(" ")" @end) @indent
5815 (_ "{" "}" @end) @indent
5816 "#,
5817 )
5818 .unwrap(),
5819 ));
5820
5821 let text = "fn a() {}";
5822
5823 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, None, cx));
5824 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
5825 let (_, editor) = cx.add_window(|cx| build_editor(buffer, settings, cx));
5826 editor
5827 .condition(&cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
5828 .await;
5829
5830 editor.update(&mut cx, |editor, cx| {
5831 editor.select_ranges([5..5, 8..8, 9..9], None, cx);
5832 editor.newline(&Newline, cx);
5833 assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
5834 assert_eq!(
5835 editor.selected_ranges(cx),
5836 &[
5837 Point::new(1, 4)..Point::new(1, 4),
5838 Point::new(3, 4)..Point::new(3, 4),
5839 Point::new(5, 0)..Point::new(5, 0)
5840 ]
5841 );
5842 });
5843 }
5844
5845 #[gpui::test]
5846 async fn test_autoclose_pairs(mut cx: gpui::TestAppContext) {
5847 let settings = cx.read(EditorSettings::test);
5848 let language = Some(Arc::new(Language::new(
5849 LanguageConfig {
5850 brackets: vec![
5851 BracketPair {
5852 start: "{".to_string(),
5853 end: "}".to_string(),
5854 close: true,
5855 newline: true,
5856 },
5857 BracketPair {
5858 start: "/*".to_string(),
5859 end: " */".to_string(),
5860 close: true,
5861 newline: true,
5862 },
5863 ],
5864 ..Default::default()
5865 },
5866 Some(tree_sitter_rust::language()),
5867 )));
5868
5869 let text = r#"
5870 a
5871
5872 /
5873
5874 "#
5875 .unindent();
5876
5877 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, None, cx));
5878 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
5879 let (_, view) = cx.add_window(|cx| build_editor(buffer, settings, cx));
5880 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5881 .await;
5882
5883 view.update(&mut cx, |view, cx| {
5884 view.select_display_ranges(
5885 &[
5886 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
5887 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
5888 ],
5889 cx,
5890 );
5891 view.handle_input(&Input("{".to_string()), cx);
5892 view.handle_input(&Input("{".to_string()), cx);
5893 view.handle_input(&Input("{".to_string()), cx);
5894 assert_eq!(
5895 view.text(cx),
5896 "
5897 {{{}}}
5898 {{{}}}
5899 /
5900
5901 "
5902 .unindent()
5903 );
5904
5905 view.move_right(&MoveRight, cx);
5906 view.handle_input(&Input("}".to_string()), cx);
5907 view.handle_input(&Input("}".to_string()), cx);
5908 view.handle_input(&Input("}".to_string()), cx);
5909 assert_eq!(
5910 view.text(cx),
5911 "
5912 {{{}}}}
5913 {{{}}}}
5914 /
5915
5916 "
5917 .unindent()
5918 );
5919
5920 view.undo(&Undo, cx);
5921 view.handle_input(&Input("/".to_string()), cx);
5922 view.handle_input(&Input("*".to_string()), cx);
5923 assert_eq!(
5924 view.text(cx),
5925 "
5926 /* */
5927 /* */
5928 /
5929
5930 "
5931 .unindent()
5932 );
5933
5934 view.undo(&Undo, cx);
5935 view.select_display_ranges(
5936 &[
5937 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
5938 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
5939 ],
5940 cx,
5941 );
5942 view.handle_input(&Input("*".to_string()), cx);
5943 assert_eq!(
5944 view.text(cx),
5945 "
5946 a
5947
5948 /*
5949 *
5950 "
5951 .unindent()
5952 );
5953 });
5954 }
5955
5956 #[gpui::test]
5957 async fn test_toggle_comment(mut cx: gpui::TestAppContext) {
5958 let settings = cx.read(EditorSettings::test);
5959 let language = Some(Arc::new(Language::new(
5960 LanguageConfig {
5961 line_comment: Some("// ".to_string()),
5962 ..Default::default()
5963 },
5964 Some(tree_sitter_rust::language()),
5965 )));
5966
5967 let text = "
5968 fn a() {
5969 //b();
5970 // c();
5971 // d();
5972 }
5973 "
5974 .unindent();
5975
5976 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, None, cx));
5977 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
5978 let (_, view) = cx.add_window(|cx| build_editor(buffer, settings, cx));
5979
5980 view.update(&mut cx, |editor, cx| {
5981 // If multiple selections intersect a line, the line is only
5982 // toggled once.
5983 editor.select_display_ranges(
5984 &[
5985 DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
5986 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
5987 ],
5988 cx,
5989 );
5990 editor.toggle_comments(&ToggleComments, cx);
5991 assert_eq!(
5992 editor.text(cx),
5993 "
5994 fn a() {
5995 b();
5996 c();
5997 d();
5998 }
5999 "
6000 .unindent()
6001 );
6002
6003 // The comment prefix is inserted at the same column for every line
6004 // in a selection.
6005 editor.select_display_ranges(&[DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)], cx);
6006 editor.toggle_comments(&ToggleComments, cx);
6007 assert_eq!(
6008 editor.text(cx),
6009 "
6010 fn a() {
6011 // b();
6012 // c();
6013 // d();
6014 }
6015 "
6016 .unindent()
6017 );
6018
6019 // If a selection ends at the beginning of a line, that line is not toggled.
6020 editor.select_display_ranges(&[DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)], cx);
6021 editor.toggle_comments(&ToggleComments, cx);
6022 assert_eq!(
6023 editor.text(cx),
6024 "
6025 fn a() {
6026 // b();
6027 c();
6028 // d();
6029 }
6030 "
6031 .unindent()
6032 );
6033 });
6034 }
6035
6036 #[gpui::test]
6037 fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
6038 let settings = EditorSettings::test(cx);
6039 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
6040 let multibuffer = cx.add_model(|cx| {
6041 let mut multibuffer = MultiBuffer::new(0);
6042 multibuffer.push_excerpt(
6043 ExcerptProperties {
6044 buffer: &buffer,
6045 range: Point::new(0, 0)..Point::new(0, 4),
6046 },
6047 cx,
6048 );
6049 multibuffer.push_excerpt(
6050 ExcerptProperties {
6051 buffer: &buffer,
6052 range: Point::new(1, 0)..Point::new(1, 4),
6053 },
6054 cx,
6055 );
6056 multibuffer
6057 });
6058
6059 assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
6060
6061 let (_, view) = cx.add_window(Default::default(), |cx| {
6062 build_editor(multibuffer, settings, cx)
6063 });
6064 view.update(cx, |view, cx| {
6065 view.select_display_ranges(
6066 &[
6067 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
6068 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
6069 ],
6070 cx,
6071 );
6072
6073 view.handle_input(&Input("X".to_string()), cx);
6074 assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
6075 assert_eq!(
6076 view.selected_display_ranges(cx),
6077 &[
6078 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
6079 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
6080 ]
6081 )
6082 });
6083 }
6084
6085 #[gpui::test]
6086 fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
6087 let settings = EditorSettings::test(cx);
6088 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
6089 let multibuffer = cx.add_model(|cx| {
6090 let mut multibuffer = MultiBuffer::new(0);
6091 multibuffer.push_excerpt(
6092 ExcerptProperties {
6093 buffer: &buffer,
6094 range: Point::new(0, 0)..Point::new(1, 4),
6095 },
6096 cx,
6097 );
6098 multibuffer.push_excerpt(
6099 ExcerptProperties {
6100 buffer: &buffer,
6101 range: Point::new(1, 0)..Point::new(2, 4),
6102 },
6103 cx,
6104 );
6105 multibuffer
6106 });
6107
6108 assert_eq!(
6109 multibuffer.read(cx).read(cx).text(),
6110 "aaaa\nbbbb\nbbbb\ncccc"
6111 );
6112
6113 let (_, view) = cx.add_window(Default::default(), |cx| {
6114 build_editor(multibuffer, settings, cx)
6115 });
6116 view.update(cx, |view, cx| {
6117 view.select_display_ranges(
6118 &[
6119 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
6120 DisplayPoint::new(2, 3)..DisplayPoint::new(2, 3),
6121 ],
6122 cx,
6123 );
6124
6125 view.handle_input(&Input("X".to_string()), cx);
6126 assert_eq!(view.text(cx), "aaaa\nbXbbXb\nbXbbXb\ncccc");
6127 assert_eq!(
6128 view.selected_display_ranges(cx),
6129 &[
6130 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
6131 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
6132 ]
6133 );
6134
6135 view.newline(&Newline, cx);
6136 assert_eq!(view.text(cx), "aaaa\nbX\nbbX\nb\nbX\nbbX\nb\ncccc");
6137 assert_eq!(
6138 view.selected_display_ranges(cx),
6139 &[
6140 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
6141 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
6142 ]
6143 );
6144 });
6145 }
6146
6147 #[gpui::test]
6148 fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
6149 let settings = EditorSettings::test(cx);
6150 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
6151 let mut excerpt1_id = None;
6152 let multibuffer = cx.add_model(|cx| {
6153 let mut multibuffer = MultiBuffer::new(0);
6154 excerpt1_id = Some(multibuffer.push_excerpt(
6155 ExcerptProperties {
6156 buffer: &buffer,
6157 range: Point::new(0, 0)..Point::new(1, 4),
6158 },
6159 cx,
6160 ));
6161 multibuffer.push_excerpt(
6162 ExcerptProperties {
6163 buffer: &buffer,
6164 range: Point::new(1, 0)..Point::new(2, 4),
6165 },
6166 cx,
6167 );
6168 multibuffer
6169 });
6170 assert_eq!(
6171 multibuffer.read(cx).read(cx).text(),
6172 "aaaa\nbbbb\nbbbb\ncccc"
6173 );
6174 let (_, editor) = cx.add_window(Default::default(), |cx| {
6175 let mut editor = build_editor(multibuffer.clone(), settings, cx);
6176 editor.select_display_ranges(
6177 &[
6178 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
6179 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
6180 ],
6181 cx,
6182 );
6183 editor
6184 });
6185
6186 // Refreshing selections is a no-op when excerpts haven't changed.
6187 editor.update(cx, |editor, cx| {
6188 editor.refresh_selections(cx);
6189 assert_eq!(
6190 editor.selected_display_ranges(cx),
6191 [
6192 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
6193 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
6194 ]
6195 );
6196 });
6197
6198 multibuffer.update(cx, |multibuffer, cx| {
6199 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
6200 });
6201 editor.update(cx, |editor, cx| {
6202 // Removing an excerpt causes the first selection to become degenerate.
6203 assert_eq!(
6204 editor.selected_display_ranges(cx),
6205 [
6206 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
6207 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
6208 ]
6209 );
6210
6211 // Refreshing selections will relocate the first selection to the original buffer
6212 // location.
6213 editor.refresh_selections(cx);
6214 assert_eq!(
6215 editor.selected_display_ranges(cx),
6216 [
6217 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
6218 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3)
6219 ]
6220 );
6221 });
6222 }
6223
6224 #[gpui::test]
6225 async fn test_extra_newline_insertion(mut cx: gpui::TestAppContext) {
6226 let settings = cx.read(EditorSettings::test);
6227 let language = Some(Arc::new(Language::new(
6228 LanguageConfig {
6229 brackets: vec![
6230 BracketPair {
6231 start: "{".to_string(),
6232 end: "}".to_string(),
6233 close: true,
6234 newline: true,
6235 },
6236 BracketPair {
6237 start: "/* ".to_string(),
6238 end: " */".to_string(),
6239 close: true,
6240 newline: true,
6241 },
6242 ],
6243 ..Default::default()
6244 },
6245 Some(tree_sitter_rust::language()),
6246 )));
6247
6248 let text = concat!(
6249 "{ }\n", // Suppress rustfmt
6250 " x\n", //
6251 " /* */\n", //
6252 "x\n", //
6253 "{{} }\n", //
6254 );
6255
6256 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, None, cx));
6257 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6258 let (_, view) = cx.add_window(|cx| build_editor(buffer, settings, cx));
6259 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
6260 .await;
6261
6262 view.update(&mut cx, |view, cx| {
6263 view.select_display_ranges(
6264 &[
6265 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
6266 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
6267 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
6268 ],
6269 cx,
6270 );
6271 view.newline(&Newline, cx);
6272
6273 assert_eq!(
6274 view.buffer().read(cx).read(cx).text(),
6275 concat!(
6276 "{ \n", // Suppress rustfmt
6277 "\n", //
6278 "}\n", //
6279 " x\n", //
6280 " /* \n", //
6281 " \n", //
6282 " */\n", //
6283 "x\n", //
6284 "{{} \n", //
6285 "}\n", //
6286 )
6287 );
6288 });
6289 }
6290
6291 fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
6292 let point = DisplayPoint::new(row as u32, column as u32);
6293 point..point
6294 }
6295
6296 fn build_editor(
6297 buffer: ModelHandle<MultiBuffer>,
6298 settings: EditorSettings,
6299 cx: &mut ViewContext<Editor>,
6300 ) -> Editor {
6301 Editor::for_buffer(buffer, Arc::new(move |_| settings.clone()), cx)
6302 }
6303}
6304
6305trait RangeExt<T> {
6306 fn sorted(&self) -> Range<T>;
6307 fn to_inclusive(&self) -> RangeInclusive<T>;
6308}
6309
6310impl<T: Ord + Clone> RangeExt<T> for Range<T> {
6311 fn sorted(&self) -> Self {
6312 cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone()
6313 }
6314
6315 fn to_inclusive(&self) -> RangeInclusive<T> {
6316 self.start.clone()..=self.end.clone()
6317 }
6318}