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(any(test, feature = "test-support"))]
1142 pub 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 let prev_newest_selection = self.selections.iter().max_by_key(|s| s.id);
3301 let curr_newest_selection = selections.iter().max_by_key(|s| s.id);
3302 if let Some((prev_selection, curr_selection)) =
3303 prev_newest_selection.zip(curr_newest_selection)
3304 {
3305 if prev_selection.head().to_offset(&buffer) != curr_selection.head().to_offset(&buffer)
3306 {
3307 self.push_to_navigation_history(cx);
3308 }
3309 }
3310
3311 self.set_selections(
3312 Arc::from_iter(selections.into_iter().map(|selection| {
3313 let end_bias = if selection.end > selection.start {
3314 Bias::Left
3315 } else {
3316 Bias::Right
3317 };
3318 Selection {
3319 id: selection.id,
3320 start: buffer.anchor_after(selection.start),
3321 end: buffer.anchor_at(selection.end, end_bias),
3322 reversed: selection.reversed,
3323 goal: selection.goal,
3324 }
3325 })),
3326 cx,
3327 );
3328 }
3329
3330 /// Compute new ranges for any selections that were located in excerpts that have
3331 /// since been removed.
3332 ///
3333 /// Returns a `HashMap` indicating which selections whose former head position
3334 /// was no longer present. The keys of the map are selection ids. The values are
3335 /// the id of the new excerpt where the head of the selection has been moved.
3336 pub fn refresh_selections(&mut self, cx: &mut ViewContext<Self>) -> HashMap<usize, ExcerptId> {
3337 let snapshot = self.buffer.read(cx).read(cx);
3338 let anchors_with_status = snapshot.refresh_anchors(
3339 self.selections
3340 .iter()
3341 .flat_map(|selection| [&selection.start, &selection.end]),
3342 );
3343 let offsets =
3344 snapshot.summaries_for_anchors::<usize, _>(anchors_with_status.iter().map(|a| &a.1));
3345 let offsets = offsets.chunks(2);
3346 let statuses = anchors_with_status
3347 .chunks(2)
3348 .map(|a| (a[0].0 / 2, a[0].2, a[1].2));
3349
3350 let mut selections_with_lost_position = HashMap::default();
3351 let new_selections = offsets
3352 .zip(statuses)
3353 .map(|(offsets, (selection_ix, kept_start, kept_end))| {
3354 let selection = &self.selections[selection_ix];
3355 let kept_head = if selection.reversed {
3356 kept_start
3357 } else {
3358 kept_end
3359 };
3360 if !kept_head {
3361 selections_with_lost_position
3362 .insert(selection.id, selection.head().excerpt_id.clone());
3363 }
3364
3365 Selection {
3366 id: selection.id,
3367 start: offsets[0],
3368 end: offsets[1],
3369 reversed: selection.reversed,
3370 goal: selection.goal,
3371 }
3372 })
3373 .collect();
3374 drop(snapshot);
3375 self.update_selections(new_selections, Some(Autoscroll::Fit), cx);
3376 selections_with_lost_position
3377 }
3378
3379 fn set_selections(&mut self, selections: Arc<[Selection<Anchor>]>, cx: &mut ViewContext<Self>) {
3380 self.selections = selections;
3381 self.buffer.update(cx, |buffer, cx| {
3382 buffer.set_active_selections(&self.selections, cx)
3383 });
3384 }
3385
3386 fn request_autoscroll(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
3387 self.autoscroll_request = Some(autoscroll);
3388 cx.notify();
3389 }
3390
3391 fn start_transaction(&mut self, cx: &mut ViewContext<Self>) {
3392 self.start_transaction_at(Instant::now(), cx);
3393 }
3394
3395 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
3396 self.end_selection(cx);
3397 if let Some(tx_id) = self
3398 .buffer
3399 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
3400 {
3401 self.selection_history
3402 .insert(tx_id, (self.selections.clone(), None));
3403 }
3404 }
3405
3406 fn end_transaction(&mut self, cx: &mut ViewContext<Self>) {
3407 self.end_transaction_at(Instant::now(), cx);
3408 }
3409
3410 fn end_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
3411 if let Some(tx_id) = self
3412 .buffer
3413 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
3414 {
3415 self.selection_history.get_mut(&tx_id).unwrap().1 = Some(self.selections.clone());
3416 }
3417 }
3418
3419 pub fn page_up(&mut self, _: &PageUp, _: &mut ViewContext<Self>) {
3420 log::info!("Editor::page_up");
3421 }
3422
3423 pub fn page_down(&mut self, _: &PageDown, _: &mut ViewContext<Self>) {
3424 log::info!("Editor::page_down");
3425 }
3426
3427 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
3428 let mut fold_ranges = Vec::new();
3429
3430 let selections = self.local_selections::<Point>(cx);
3431 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3432 for selection in selections {
3433 let range = selection.display_range(&display_map).sorted();
3434 let buffer_start_row = range.start.to_point(&display_map).row;
3435
3436 for row in (0..=range.end.row()).rev() {
3437 if self.is_line_foldable(&display_map, row) && !display_map.is_line_folded(row) {
3438 let fold_range = self.foldable_range_for_line(&display_map, row);
3439 if fold_range.end.row >= buffer_start_row {
3440 fold_ranges.push(fold_range);
3441 if row <= range.start.row() {
3442 break;
3443 }
3444 }
3445 }
3446 }
3447 }
3448
3449 self.fold_ranges(fold_ranges, cx);
3450 }
3451
3452 pub fn unfold(&mut self, _: &Unfold, cx: &mut ViewContext<Self>) {
3453 let selections = self.local_selections::<Point>(cx);
3454 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3455 let buffer = &display_map.buffer_snapshot;
3456 let ranges = selections
3457 .iter()
3458 .map(|s| {
3459 let range = s.display_range(&display_map).sorted();
3460 let mut start = range.start.to_point(&display_map);
3461 let mut end = range.end.to_point(&display_map);
3462 start.column = 0;
3463 end.column = buffer.line_len(end.row);
3464 start..end
3465 })
3466 .collect::<Vec<_>>();
3467 self.unfold_ranges(ranges, cx);
3468 }
3469
3470 fn is_line_foldable(&self, display_map: &DisplaySnapshot, display_row: u32) -> bool {
3471 let max_point = display_map.max_point();
3472 if display_row >= max_point.row() {
3473 false
3474 } else {
3475 let (start_indent, is_blank) = display_map.line_indent(display_row);
3476 if is_blank {
3477 false
3478 } else {
3479 for display_row in display_row + 1..=max_point.row() {
3480 let (indent, is_blank) = display_map.line_indent(display_row);
3481 if !is_blank {
3482 return indent > start_indent;
3483 }
3484 }
3485 false
3486 }
3487 }
3488 }
3489
3490 fn foldable_range_for_line(
3491 &self,
3492 display_map: &DisplaySnapshot,
3493 start_row: u32,
3494 ) -> Range<Point> {
3495 let max_point = display_map.max_point();
3496
3497 let (start_indent, _) = display_map.line_indent(start_row);
3498 let start = DisplayPoint::new(start_row, display_map.line_len(start_row));
3499 let mut end = None;
3500 for row in start_row + 1..=max_point.row() {
3501 let (indent, is_blank) = display_map.line_indent(row);
3502 if !is_blank && indent <= start_indent {
3503 end = Some(DisplayPoint::new(row - 1, display_map.line_len(row - 1)));
3504 break;
3505 }
3506 }
3507
3508 let end = end.unwrap_or(max_point);
3509 return start.to_point(display_map)..end.to_point(display_map);
3510 }
3511
3512 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
3513 let selections = self.local_selections::<Point>(cx);
3514 let ranges = selections.into_iter().map(|s| s.start..s.end);
3515 self.fold_ranges(ranges, cx);
3516 }
3517
3518 fn fold_ranges<T: ToOffset>(
3519 &mut self,
3520 ranges: impl IntoIterator<Item = Range<T>>,
3521 cx: &mut ViewContext<Self>,
3522 ) {
3523 let mut ranges = ranges.into_iter().peekable();
3524 if ranges.peek().is_some() {
3525 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
3526 self.request_autoscroll(Autoscroll::Fit, cx);
3527 cx.notify();
3528 }
3529 }
3530
3531 fn unfold_ranges<T: ToOffset>(&mut self, ranges: Vec<Range<T>>, cx: &mut ViewContext<Self>) {
3532 if !ranges.is_empty() {
3533 self.display_map
3534 .update(cx, |map, cx| map.unfold(ranges, cx));
3535 self.request_autoscroll(Autoscroll::Fit, cx);
3536 cx.notify();
3537 }
3538 }
3539
3540 pub fn insert_blocks(
3541 &mut self,
3542 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
3543 cx: &mut ViewContext<Self>,
3544 ) -> Vec<BlockId> {
3545 let blocks = self
3546 .display_map
3547 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
3548 self.request_autoscroll(Autoscroll::Fit, cx);
3549 blocks
3550 }
3551
3552 pub fn replace_blocks(
3553 &mut self,
3554 blocks: HashMap<BlockId, RenderBlock>,
3555 cx: &mut ViewContext<Self>,
3556 ) {
3557 self.display_map
3558 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
3559 self.request_autoscroll(Autoscroll::Fit, cx);
3560 }
3561
3562 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
3563 self.display_map.update(cx, |display_map, cx| {
3564 display_map.remove_blocks(block_ids, cx)
3565 });
3566 }
3567
3568 pub fn longest_row(&self, cx: &mut MutableAppContext) -> u32 {
3569 self.display_map
3570 .update(cx, |map, cx| map.snapshot(cx))
3571 .longest_row()
3572 }
3573
3574 pub fn max_point(&self, cx: &mut MutableAppContext) -> DisplayPoint {
3575 self.display_map
3576 .update(cx, |map, cx| map.snapshot(cx))
3577 .max_point()
3578 }
3579
3580 pub fn text(&self, cx: &AppContext) -> String {
3581 self.buffer.read(cx).read(cx).text()
3582 }
3583
3584 pub fn display_text(&self, cx: &mut MutableAppContext) -> String {
3585 self.display_map
3586 .update(cx, |map, cx| map.snapshot(cx))
3587 .text()
3588 }
3589
3590 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut MutableAppContext) -> bool {
3591 self.display_map
3592 .update(cx, |map, cx| map.set_wrap_width(width, cx))
3593 }
3594
3595 pub fn set_highlighted_rows(&mut self, rows: Option<Range<u32>>) {
3596 self.highlighted_rows = rows;
3597 }
3598
3599 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
3600 self.highlighted_rows.clone()
3601 }
3602
3603 fn next_blink_epoch(&mut self) -> usize {
3604 self.blink_epoch += 1;
3605 self.blink_epoch
3606 }
3607
3608 fn pause_cursor_blinking(&mut self, cx: &mut ViewContext<Self>) {
3609 self.show_local_cursors = true;
3610 cx.notify();
3611
3612 let epoch = self.next_blink_epoch();
3613 cx.spawn(|this, mut cx| {
3614 let this = this.downgrade();
3615 async move {
3616 Timer::after(CURSOR_BLINK_INTERVAL).await;
3617 if let Some(this) = cx.read(|cx| this.upgrade(cx)) {
3618 this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
3619 }
3620 }
3621 })
3622 .detach();
3623 }
3624
3625 fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
3626 if epoch == self.blink_epoch {
3627 self.blinking_paused = false;
3628 self.blink_cursors(epoch, cx);
3629 }
3630 }
3631
3632 fn blink_cursors(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
3633 if epoch == self.blink_epoch && self.focused && !self.blinking_paused {
3634 self.show_local_cursors = !self.show_local_cursors;
3635 cx.notify();
3636
3637 let epoch = self.next_blink_epoch();
3638 cx.spawn(|this, mut cx| {
3639 let this = this.downgrade();
3640 async move {
3641 Timer::after(CURSOR_BLINK_INTERVAL).await;
3642 if let Some(this) = cx.read(|cx| this.upgrade(cx)) {
3643 this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx));
3644 }
3645 }
3646 })
3647 .detach();
3648 }
3649 }
3650
3651 pub fn show_local_cursors(&self) -> bool {
3652 self.show_local_cursors
3653 }
3654
3655 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
3656 self.refresh_active_diagnostics(cx);
3657 cx.notify();
3658 }
3659
3660 fn on_buffer_event(
3661 &mut self,
3662 _: ModelHandle<MultiBuffer>,
3663 event: &language::Event,
3664 cx: &mut ViewContext<Self>,
3665 ) {
3666 match event {
3667 language::Event::Edited => cx.emit(Event::Edited),
3668 language::Event::Dirtied => cx.emit(Event::Dirtied),
3669 language::Event::Saved => cx.emit(Event::Saved),
3670 language::Event::FileHandleChanged => cx.emit(Event::FileHandleChanged),
3671 language::Event::Reloaded => cx.emit(Event::FileHandleChanged),
3672 language::Event::Closed => cx.emit(Event::Closed),
3673 _ => {}
3674 }
3675 }
3676
3677 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
3678 cx.notify();
3679 }
3680}
3681
3682impl EditorSnapshot {
3683 pub fn is_focused(&self) -> bool {
3684 self.is_focused
3685 }
3686
3687 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
3688 self.placeholder_text.as_ref()
3689 }
3690
3691 pub fn scroll_position(&self) -> Vector2F {
3692 compute_scroll_position(
3693 &self.display_snapshot,
3694 self.scroll_position,
3695 &self.scroll_top_anchor,
3696 )
3697 }
3698}
3699
3700impl Deref for EditorSnapshot {
3701 type Target = DisplaySnapshot;
3702
3703 fn deref(&self) -> &Self::Target {
3704 &self.display_snapshot
3705 }
3706}
3707
3708impl EditorSettings {
3709 #[cfg(any(test, feature = "test-support"))]
3710 pub fn test(cx: &AppContext) -> Self {
3711 Self {
3712 tab_size: 4,
3713 soft_wrap: SoftWrap::None,
3714 style: {
3715 let font_cache: &gpui::FontCache = cx.font_cache();
3716 let font_family_name = Arc::from("Monaco");
3717 let font_properties = Default::default();
3718 let font_family_id = font_cache.load_family(&[&font_family_name]).unwrap();
3719 let font_id = font_cache
3720 .select_font(font_family_id, &font_properties)
3721 .unwrap();
3722 EditorStyle {
3723 text: gpui::fonts::TextStyle {
3724 font_family_name,
3725 font_family_id,
3726 font_id,
3727 font_size: 14.,
3728 color: gpui::color::Color::from_u32(0xff0000ff),
3729 font_properties,
3730 underline: None,
3731 },
3732 placeholder_text: None,
3733 background: Default::default(),
3734 gutter_background: Default::default(),
3735 active_line_background: Default::default(),
3736 highlighted_line_background: Default::default(),
3737 line_number: Default::default(),
3738 line_number_active: Default::default(),
3739 selection: Default::default(),
3740 guest_selections: Default::default(),
3741 syntax: Default::default(),
3742 diagnostic_path_header: Default::default(),
3743 error_diagnostic: Default::default(),
3744 invalid_error_diagnostic: Default::default(),
3745 warning_diagnostic: Default::default(),
3746 invalid_warning_diagnostic: Default::default(),
3747 information_diagnostic: Default::default(),
3748 invalid_information_diagnostic: Default::default(),
3749 hint_diagnostic: Default::default(),
3750 invalid_hint_diagnostic: Default::default(),
3751 }
3752 },
3753 }
3754 }
3755}
3756
3757fn compute_scroll_position(
3758 snapshot: &DisplaySnapshot,
3759 mut scroll_position: Vector2F,
3760 scroll_top_anchor: &Option<Anchor>,
3761) -> Vector2F {
3762 if let Some(anchor) = scroll_top_anchor {
3763 let scroll_top = anchor.to_display_point(snapshot).row() as f32;
3764 scroll_position.set_y(scroll_top + scroll_position.y());
3765 } else {
3766 scroll_position.set_y(0.);
3767 }
3768 scroll_position
3769}
3770
3771#[derive(Copy, Clone)]
3772pub enum Event {
3773 Activate,
3774 Edited,
3775 Blurred,
3776 Dirtied,
3777 Saved,
3778 FileHandleChanged,
3779 Closed,
3780}
3781
3782impl Entity for Editor {
3783 type Event = Event;
3784}
3785
3786impl View for Editor {
3787 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
3788 let settings = (self.build_settings)(cx);
3789 self.display_map.update(cx, |map, cx| {
3790 map.set_font(
3791 settings.style.text.font_id,
3792 settings.style.text.font_size,
3793 cx,
3794 )
3795 });
3796 EditorElement::new(self.handle.clone(), settings).boxed()
3797 }
3798
3799 fn ui_name() -> &'static str {
3800 "Editor"
3801 }
3802
3803 fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
3804 self.focused = true;
3805 self.blink_cursors(self.blink_epoch, cx);
3806 self.buffer.update(cx, |buffer, cx| {
3807 buffer.set_active_selections(&self.selections, cx)
3808 });
3809 }
3810
3811 fn on_blur(&mut self, cx: &mut ViewContext<Self>) {
3812 self.focused = false;
3813 self.show_local_cursors = false;
3814 self.buffer
3815 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
3816 cx.emit(Event::Blurred);
3817 cx.notify();
3818 }
3819
3820 fn keymap_context(&self, _: &AppContext) -> gpui::keymap::Context {
3821 let mut cx = Self::default_keymap_context();
3822 let mode = match self.mode {
3823 EditorMode::SingleLine => "single_line",
3824 EditorMode::AutoHeight { .. } => "auto_height",
3825 EditorMode::Full => "full",
3826 };
3827 cx.map.insert("mode".into(), mode.into());
3828 cx
3829 }
3830}
3831
3832impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
3833 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
3834 let start = self.start.to_point(buffer);
3835 let end = self.end.to_point(buffer);
3836 if self.reversed {
3837 end..start
3838 } else {
3839 start..end
3840 }
3841 }
3842
3843 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
3844 let start = self.start.to_offset(buffer);
3845 let end = self.end.to_offset(buffer);
3846 if self.reversed {
3847 end..start
3848 } else {
3849 start..end
3850 }
3851 }
3852
3853 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
3854 let start = self
3855 .start
3856 .to_point(&map.buffer_snapshot)
3857 .to_display_point(map);
3858 let end = self
3859 .end
3860 .to_point(&map.buffer_snapshot)
3861 .to_display_point(map);
3862 if self.reversed {
3863 end..start
3864 } else {
3865 start..end
3866 }
3867 }
3868
3869 fn spanned_rows(
3870 &self,
3871 include_end_if_at_line_start: bool,
3872 map: &DisplaySnapshot,
3873 ) -> Range<u32> {
3874 let start = self.start.to_point(&map.buffer_snapshot);
3875 let mut end = self.end.to_point(&map.buffer_snapshot);
3876 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
3877 end.row -= 1;
3878 }
3879
3880 let buffer_start = map.prev_line_boundary(start).0;
3881 let buffer_end = map.next_line_boundary(end).0;
3882 buffer_start.row..buffer_end.row + 1
3883 }
3884}
3885
3886pub fn diagnostic_block_renderer(
3887 diagnostic: Diagnostic,
3888 is_valid: bool,
3889 build_settings: BuildSettings,
3890) -> RenderBlock {
3891 Arc::new(move |cx: &BlockContext| {
3892 let settings = build_settings(cx);
3893 let mut text_style = settings.style.text.clone();
3894 text_style.color = diagnostic_style(diagnostic.severity, is_valid, &settings.style).text;
3895 Text::new(diagnostic.message.clone(), text_style)
3896 .with_soft_wrap(false)
3897 .contained()
3898 .with_margin_left(cx.anchor_x)
3899 .boxed()
3900 })
3901}
3902
3903pub fn diagnostic_style(
3904 severity: DiagnosticSeverity,
3905 valid: bool,
3906 style: &EditorStyle,
3907) -> DiagnosticStyle {
3908 match (severity, valid) {
3909 (DiagnosticSeverity::ERROR, true) => style.error_diagnostic,
3910 (DiagnosticSeverity::ERROR, false) => style.invalid_error_diagnostic,
3911 (DiagnosticSeverity::WARNING, true) => style.warning_diagnostic,
3912 (DiagnosticSeverity::WARNING, false) => style.invalid_warning_diagnostic,
3913 (DiagnosticSeverity::INFORMATION, true) => style.information_diagnostic,
3914 (DiagnosticSeverity::INFORMATION, false) => style.invalid_information_diagnostic,
3915 (DiagnosticSeverity::HINT, true) => style.hint_diagnostic,
3916 (DiagnosticSeverity::HINT, false) => style.invalid_hint_diagnostic,
3917 _ => Default::default(),
3918 }
3919}
3920
3921pub fn settings_builder(
3922 buffer: WeakModelHandle<MultiBuffer>,
3923 settings: watch::Receiver<workspace::Settings>,
3924) -> BuildSettings {
3925 Arc::new(move |cx| {
3926 let settings = settings.borrow();
3927 let font_cache = cx.font_cache();
3928 let font_family_id = settings.buffer_font_family;
3929 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
3930 let font_properties = Default::default();
3931 let font_id = font_cache
3932 .select_font(font_family_id, &font_properties)
3933 .unwrap();
3934 let font_size = settings.buffer_font_size;
3935
3936 let mut theme = settings.theme.editor.clone();
3937 theme.text = TextStyle {
3938 color: theme.text.color,
3939 font_family_name,
3940 font_family_id,
3941 font_id,
3942 font_size,
3943 font_properties,
3944 underline: None,
3945 };
3946 let language = buffer.upgrade(cx).and_then(|buf| buf.read(cx).language(cx));
3947 let soft_wrap = match settings.soft_wrap(language) {
3948 workspace::settings::SoftWrap::None => SoftWrap::None,
3949 workspace::settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
3950 workspace::settings::SoftWrap::PreferredLineLength => {
3951 SoftWrap::Column(settings.preferred_line_length(language).saturating_sub(1))
3952 }
3953 };
3954
3955 EditorSettings {
3956 tab_size: settings.tab_size,
3957 soft_wrap,
3958 style: theme,
3959 }
3960 })
3961}
3962
3963#[cfg(test)]
3964mod tests {
3965 use super::*;
3966 use language::LanguageConfig;
3967 use std::time::Instant;
3968 use text::Point;
3969 use unindent::Unindent;
3970 use util::test::sample_text;
3971
3972 #[gpui::test]
3973 fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
3974 let mut now = Instant::now();
3975 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
3976 let group_interval = buffer.read(cx).transaction_group_interval();
3977 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
3978 let settings = EditorSettings::test(cx);
3979 let (_, editor) = cx.add_window(Default::default(), |cx| {
3980 build_editor(buffer.clone(), settings, cx)
3981 });
3982
3983 editor.update(cx, |editor, cx| {
3984 editor.start_transaction_at(now, cx);
3985 editor.select_ranges([2..4], None, cx);
3986 editor.insert("cd", cx);
3987 editor.end_transaction_at(now, cx);
3988 assert_eq!(editor.text(cx), "12cd56");
3989 assert_eq!(editor.selected_ranges(cx), vec![4..4]);
3990
3991 editor.start_transaction_at(now, cx);
3992 editor.select_ranges([4..5], None, cx);
3993 editor.insert("e", cx);
3994 editor.end_transaction_at(now, cx);
3995 assert_eq!(editor.text(cx), "12cde6");
3996 assert_eq!(editor.selected_ranges(cx), vec![5..5]);
3997
3998 now += group_interval + Duration::from_millis(1);
3999 editor.select_ranges([2..2], None, cx);
4000
4001 // Simulate an edit in another editor
4002 buffer.update(cx, |buffer, cx| {
4003 buffer.start_transaction_at(now, cx);
4004 buffer.edit([0..1], "a", cx);
4005 buffer.edit([1..1], "b", cx);
4006 buffer.end_transaction_at(now, cx);
4007 });
4008
4009 assert_eq!(editor.text(cx), "ab2cde6");
4010 assert_eq!(editor.selected_ranges(cx), vec![3..3]);
4011
4012 // Last transaction happened past the group interval in a different editor.
4013 // Undo it individually and don't restore selections.
4014 editor.undo(&Undo, cx);
4015 assert_eq!(editor.text(cx), "12cde6");
4016 assert_eq!(editor.selected_ranges(cx), vec![2..2]);
4017
4018 // First two transactions happened within the group interval in this editor.
4019 // Undo them together and restore selections.
4020 editor.undo(&Undo, cx);
4021 editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
4022 assert_eq!(editor.text(cx), "123456");
4023 assert_eq!(editor.selected_ranges(cx), vec![0..0]);
4024
4025 // Redo the first two transactions together.
4026 editor.redo(&Redo, cx);
4027 assert_eq!(editor.text(cx), "12cde6");
4028 assert_eq!(editor.selected_ranges(cx), vec![5..5]);
4029
4030 // Redo the last transaction on its own.
4031 editor.redo(&Redo, cx);
4032 assert_eq!(editor.text(cx), "ab2cde6");
4033 assert_eq!(editor.selected_ranges(cx), vec![6..6]);
4034
4035 // Test empty transactions.
4036 editor.start_transaction_at(now, cx);
4037 editor.end_transaction_at(now, cx);
4038 editor.undo(&Undo, cx);
4039 assert_eq!(editor.text(cx), "12cde6");
4040 });
4041 }
4042
4043 #[gpui::test]
4044 fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
4045 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
4046 let settings = EditorSettings::test(cx);
4047 let (_, editor) =
4048 cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
4049
4050 editor.update(cx, |view, cx| {
4051 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
4052 });
4053
4054 assert_eq!(
4055 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
4056 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
4057 );
4058
4059 editor.update(cx, |view, cx| {
4060 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
4061 });
4062
4063 assert_eq!(
4064 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
4065 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
4066 );
4067
4068 editor.update(cx, |view, cx| {
4069 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
4070 });
4071
4072 assert_eq!(
4073 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
4074 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
4075 );
4076
4077 editor.update(cx, |view, cx| {
4078 view.end_selection(cx);
4079 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
4080 });
4081
4082 assert_eq!(
4083 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
4084 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
4085 );
4086
4087 editor.update(cx, |view, cx| {
4088 view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
4089 view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
4090 });
4091
4092 assert_eq!(
4093 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
4094 [
4095 DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
4096 DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
4097 ]
4098 );
4099
4100 editor.update(cx, |view, cx| {
4101 view.end_selection(cx);
4102 });
4103
4104 assert_eq!(
4105 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
4106 [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
4107 );
4108 }
4109
4110 #[gpui::test]
4111 fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
4112 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
4113 let settings = EditorSettings::test(cx);
4114 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
4115
4116 view.update(cx, |view, cx| {
4117 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
4118 assert_eq!(
4119 view.selected_display_ranges(cx),
4120 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
4121 );
4122 });
4123
4124 view.update(cx, |view, cx| {
4125 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
4126 assert_eq!(
4127 view.selected_display_ranges(cx),
4128 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
4129 );
4130 });
4131
4132 view.update(cx, |view, cx| {
4133 view.cancel(&Cancel, cx);
4134 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
4135 assert_eq!(
4136 view.selected_display_ranges(cx),
4137 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
4138 );
4139 });
4140 }
4141
4142 #[gpui::test]
4143 fn test_cancel(cx: &mut gpui::MutableAppContext) {
4144 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
4145 let settings = EditorSettings::test(cx);
4146 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
4147
4148 view.update(cx, |view, cx| {
4149 view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
4150 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
4151 view.end_selection(cx);
4152
4153 view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
4154 view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
4155 view.end_selection(cx);
4156 assert_eq!(
4157 view.selected_display_ranges(cx),
4158 [
4159 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
4160 DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
4161 ]
4162 );
4163 });
4164
4165 view.update(cx, |view, cx| {
4166 view.cancel(&Cancel, cx);
4167 assert_eq!(
4168 view.selected_display_ranges(cx),
4169 [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
4170 );
4171 });
4172
4173 view.update(cx, |view, cx| {
4174 view.cancel(&Cancel, cx);
4175 assert_eq!(
4176 view.selected_display_ranges(cx),
4177 [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
4178 );
4179 });
4180 }
4181
4182 #[gpui::test]
4183 fn test_fold(cx: &mut gpui::MutableAppContext) {
4184 let buffer = MultiBuffer::build_simple(
4185 &"
4186 impl Foo {
4187 // Hello!
4188
4189 fn a() {
4190 1
4191 }
4192
4193 fn b() {
4194 2
4195 }
4196
4197 fn c() {
4198 3
4199 }
4200 }
4201 "
4202 .unindent(),
4203 cx,
4204 );
4205 let settings = EditorSettings::test(&cx);
4206 let (_, view) = cx.add_window(Default::default(), |cx| {
4207 build_editor(buffer.clone(), settings, cx)
4208 });
4209
4210 view.update(cx, |view, cx| {
4211 view.select_display_ranges(&[DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)], cx);
4212 view.fold(&Fold, cx);
4213 assert_eq!(
4214 view.display_text(cx),
4215 "
4216 impl Foo {
4217 // Hello!
4218
4219 fn a() {
4220 1
4221 }
4222
4223 fn b() {…
4224 }
4225
4226 fn c() {…
4227 }
4228 }
4229 "
4230 .unindent(),
4231 );
4232
4233 view.fold(&Fold, cx);
4234 assert_eq!(
4235 view.display_text(cx),
4236 "
4237 impl Foo {…
4238 }
4239 "
4240 .unindent(),
4241 );
4242
4243 view.unfold(&Unfold, cx);
4244 assert_eq!(
4245 view.display_text(cx),
4246 "
4247 impl Foo {
4248 // Hello!
4249
4250 fn a() {
4251 1
4252 }
4253
4254 fn b() {…
4255 }
4256
4257 fn c() {…
4258 }
4259 }
4260 "
4261 .unindent(),
4262 );
4263
4264 view.unfold(&Unfold, cx);
4265 assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
4266 });
4267 }
4268
4269 #[gpui::test]
4270 fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
4271 let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
4272 let settings = EditorSettings::test(&cx);
4273 let (_, view) = cx.add_window(Default::default(), |cx| {
4274 build_editor(buffer.clone(), settings, cx)
4275 });
4276
4277 buffer.update(cx, |buffer, cx| {
4278 buffer.edit(
4279 vec![
4280 Point::new(1, 0)..Point::new(1, 0),
4281 Point::new(1, 1)..Point::new(1, 1),
4282 ],
4283 "\t",
4284 cx,
4285 );
4286 });
4287
4288 view.update(cx, |view, cx| {
4289 assert_eq!(
4290 view.selected_display_ranges(cx),
4291 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
4292 );
4293
4294 view.move_down(&MoveDown, cx);
4295 assert_eq!(
4296 view.selected_display_ranges(cx),
4297 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
4298 );
4299
4300 view.move_right(&MoveRight, cx);
4301 assert_eq!(
4302 view.selected_display_ranges(cx),
4303 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
4304 );
4305
4306 view.move_left(&MoveLeft, cx);
4307 assert_eq!(
4308 view.selected_display_ranges(cx),
4309 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
4310 );
4311
4312 view.move_up(&MoveUp, cx);
4313 assert_eq!(
4314 view.selected_display_ranges(cx),
4315 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
4316 );
4317
4318 view.move_to_end(&MoveToEnd, cx);
4319 assert_eq!(
4320 view.selected_display_ranges(cx),
4321 &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
4322 );
4323
4324 view.move_to_beginning(&MoveToBeginning, cx);
4325 assert_eq!(
4326 view.selected_display_ranges(cx),
4327 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
4328 );
4329
4330 view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)], cx);
4331 view.select_to_beginning(&SelectToBeginning, cx);
4332 assert_eq!(
4333 view.selected_display_ranges(cx),
4334 &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
4335 );
4336
4337 view.select_to_end(&SelectToEnd, cx);
4338 assert_eq!(
4339 view.selected_display_ranges(cx),
4340 &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
4341 );
4342 });
4343 }
4344
4345 #[gpui::test]
4346 fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
4347 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
4348 let settings = EditorSettings::test(&cx);
4349 let (_, view) = cx.add_window(Default::default(), |cx| {
4350 build_editor(buffer.clone(), settings, cx)
4351 });
4352
4353 assert_eq!('ⓐ'.len_utf8(), 3);
4354 assert_eq!('α'.len_utf8(), 2);
4355
4356 view.update(cx, |view, cx| {
4357 view.fold_ranges(
4358 vec![
4359 Point::new(0, 6)..Point::new(0, 12),
4360 Point::new(1, 2)..Point::new(1, 4),
4361 Point::new(2, 4)..Point::new(2, 8),
4362 ],
4363 cx,
4364 );
4365 assert_eq!(view.display_text(cx), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\n");
4366
4367 view.move_right(&MoveRight, cx);
4368 assert_eq!(
4369 view.selected_display_ranges(cx),
4370 &[empty_range(0, "ⓐ".len())]
4371 );
4372 view.move_right(&MoveRight, cx);
4373 assert_eq!(
4374 view.selected_display_ranges(cx),
4375 &[empty_range(0, "ⓐⓑ".len())]
4376 );
4377 view.move_right(&MoveRight, cx);
4378 assert_eq!(
4379 view.selected_display_ranges(cx),
4380 &[empty_range(0, "ⓐⓑ…".len())]
4381 );
4382
4383 view.move_down(&MoveDown, cx);
4384 assert_eq!(
4385 view.selected_display_ranges(cx),
4386 &[empty_range(1, "ab…".len())]
4387 );
4388 view.move_left(&MoveLeft, cx);
4389 assert_eq!(
4390 view.selected_display_ranges(cx),
4391 &[empty_range(1, "ab".len())]
4392 );
4393 view.move_left(&MoveLeft, cx);
4394 assert_eq!(
4395 view.selected_display_ranges(cx),
4396 &[empty_range(1, "a".len())]
4397 );
4398
4399 view.move_down(&MoveDown, cx);
4400 assert_eq!(
4401 view.selected_display_ranges(cx),
4402 &[empty_range(2, "α".len())]
4403 );
4404 view.move_right(&MoveRight, cx);
4405 assert_eq!(
4406 view.selected_display_ranges(cx),
4407 &[empty_range(2, "αβ".len())]
4408 );
4409 view.move_right(&MoveRight, cx);
4410 assert_eq!(
4411 view.selected_display_ranges(cx),
4412 &[empty_range(2, "αβ…".len())]
4413 );
4414 view.move_right(&MoveRight, cx);
4415 assert_eq!(
4416 view.selected_display_ranges(cx),
4417 &[empty_range(2, "αβ…ε".len())]
4418 );
4419
4420 view.move_up(&MoveUp, cx);
4421 assert_eq!(
4422 view.selected_display_ranges(cx),
4423 &[empty_range(1, "ab…e".len())]
4424 );
4425 view.move_up(&MoveUp, cx);
4426 assert_eq!(
4427 view.selected_display_ranges(cx),
4428 &[empty_range(0, "ⓐⓑ…ⓔ".len())]
4429 );
4430 view.move_left(&MoveLeft, cx);
4431 assert_eq!(
4432 view.selected_display_ranges(cx),
4433 &[empty_range(0, "ⓐⓑ…".len())]
4434 );
4435 view.move_left(&MoveLeft, cx);
4436 assert_eq!(
4437 view.selected_display_ranges(cx),
4438 &[empty_range(0, "ⓐⓑ".len())]
4439 );
4440 view.move_left(&MoveLeft, cx);
4441 assert_eq!(
4442 view.selected_display_ranges(cx),
4443 &[empty_range(0, "ⓐ".len())]
4444 );
4445 });
4446 }
4447
4448 #[gpui::test]
4449 fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
4450 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
4451 let settings = EditorSettings::test(&cx);
4452 let (_, view) = cx.add_window(Default::default(), |cx| {
4453 build_editor(buffer.clone(), settings, cx)
4454 });
4455 view.update(cx, |view, cx| {
4456 view.select_display_ranges(&[empty_range(0, "ⓐⓑⓒⓓⓔ".len())], cx);
4457 view.move_down(&MoveDown, cx);
4458 assert_eq!(
4459 view.selected_display_ranges(cx),
4460 &[empty_range(1, "abcd".len())]
4461 );
4462
4463 view.move_down(&MoveDown, cx);
4464 assert_eq!(
4465 view.selected_display_ranges(cx),
4466 &[empty_range(2, "αβγ".len())]
4467 );
4468
4469 view.move_down(&MoveDown, cx);
4470 assert_eq!(
4471 view.selected_display_ranges(cx),
4472 &[empty_range(3, "abcd".len())]
4473 );
4474
4475 view.move_down(&MoveDown, cx);
4476 assert_eq!(
4477 view.selected_display_ranges(cx),
4478 &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
4479 );
4480
4481 view.move_up(&MoveUp, cx);
4482 assert_eq!(
4483 view.selected_display_ranges(cx),
4484 &[empty_range(3, "abcd".len())]
4485 );
4486
4487 view.move_up(&MoveUp, cx);
4488 assert_eq!(
4489 view.selected_display_ranges(cx),
4490 &[empty_range(2, "αβγ".len())]
4491 );
4492 });
4493 }
4494
4495 #[gpui::test]
4496 fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
4497 let buffer = MultiBuffer::build_simple("abc\n def", cx);
4498 let settings = EditorSettings::test(&cx);
4499 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
4500 view.update(cx, |view, cx| {
4501 view.select_display_ranges(
4502 &[
4503 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
4504 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
4505 ],
4506 cx,
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, 2)..DisplayPoint::new(1, 2),
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, 0)..DisplayPoint::new(1, 0),
4528 ]
4529 );
4530 });
4531
4532 view.update(cx, |view, cx| {
4533 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
4534 assert_eq!(
4535 view.selected_display_ranges(cx),
4536 &[
4537 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
4538 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
4539 ]
4540 );
4541 });
4542
4543 view.update(cx, |view, cx| {
4544 view.move_to_end_of_line(&MoveToEndOfLine, cx);
4545 assert_eq!(
4546 view.selected_display_ranges(cx),
4547 &[
4548 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
4549 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
4550 ]
4551 );
4552 });
4553
4554 // Moving to the end of line again is a no-op.
4555 view.update(cx, |view, cx| {
4556 view.move_to_end_of_line(&MoveToEndOfLine, cx);
4557 assert_eq!(
4558 view.selected_display_ranges(cx),
4559 &[
4560 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
4561 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
4562 ]
4563 );
4564 });
4565
4566 view.update(cx, |view, cx| {
4567 view.move_left(&MoveLeft, 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, 2),
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, 0),
4585 ]
4586 );
4587 });
4588
4589 view.update(cx, |view, cx| {
4590 view.select_to_beginning_of_line(&SelectToBeginningOfLine(true), cx);
4591 assert_eq!(
4592 view.selected_display_ranges(cx),
4593 &[
4594 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
4595 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
4596 ]
4597 );
4598 });
4599
4600 view.update(cx, |view, cx| {
4601 view.select_to_end_of_line(&SelectToEndOfLine, cx);
4602 assert_eq!(
4603 view.selected_display_ranges(cx),
4604 &[
4605 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
4606 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
4607 ]
4608 );
4609 });
4610
4611 view.update(cx, |view, cx| {
4612 view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
4613 assert_eq!(view.display_text(cx), "ab\n de");
4614 assert_eq!(
4615 view.selected_display_ranges(cx),
4616 &[
4617 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
4618 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
4619 ]
4620 );
4621 });
4622
4623 view.update(cx, |view, cx| {
4624 view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
4625 assert_eq!(view.display_text(cx), "\n");
4626 assert_eq!(
4627 view.selected_display_ranges(cx),
4628 &[
4629 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
4630 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
4631 ]
4632 );
4633 });
4634 }
4635
4636 #[gpui::test]
4637 fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
4638 let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
4639 let settings = EditorSettings::test(&cx);
4640 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
4641 view.update(cx, |view, cx| {
4642 view.select_display_ranges(
4643 &[
4644 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
4645 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
4646 ],
4647 cx,
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, 9)..DisplayPoint::new(0, 9),
4657 DisplayPoint::new(2, 3)..DisplayPoint::new(2, 3),
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, 7)..DisplayPoint::new(0, 7),
4668 DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2),
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, 4)..DisplayPoint::new(0, 4),
4679 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 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(1, 0)..DisplayPoint::new(1, 0),
4691 ]
4692 );
4693 });
4694
4695 view.update(cx, |view, cx| {
4696 view.move_to_previous_word_boundary(&MoveToPreviousWordBoundary, cx);
4697 assert_eq!(
4698 view.selected_display_ranges(cx),
4699 &[
4700 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
4701 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 23),
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, 3)..DisplayPoint::new(0, 3),
4712 DisplayPoint::new(0, 24)..DisplayPoint::new(0, 24),
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, 7)..DisplayPoint::new(0, 7),
4723 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
4724 ]
4725 );
4726 });
4727
4728 view.update(cx, |view, cx| {
4729 view.move_to_next_word_boundary(&MoveToNextWordBoundary, cx);
4730 assert_eq!(
4731 view.selected_display_ranges(cx),
4732 &[
4733 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 9),
4734 DisplayPoint::new(2, 3)..DisplayPoint::new(2, 3),
4735 ]
4736 );
4737 });
4738
4739 view.update(cx, |view, cx| {
4740 view.move_right(&MoveRight, 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, 9),
4746 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 3),
4747 ]
4748 );
4749 });
4750
4751 view.update(cx, |view, cx| {
4752 view.select_to_previous_word_boundary(&SelectToPreviousWordBoundary, cx);
4753 assert_eq!(
4754 view.selected_display_ranges(cx),
4755 &[
4756 DisplayPoint::new(0, 10)..DisplayPoint::new(0, 7),
4757 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 2),
4758 ]
4759 );
4760 });
4761
4762 view.update(cx, |view, cx| {
4763 view.select_to_next_word_boundary(&SelectToNextWordBoundary, cx);
4764 assert_eq!(
4765 view.selected_display_ranges(cx),
4766 &[
4767 DisplayPoint::new(0, 10)..DisplayPoint::new(0, 9),
4768 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 3),
4769 ]
4770 );
4771 });
4772 }
4773
4774 #[gpui::test]
4775 fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
4776 let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
4777 let settings = EditorSettings::test(&cx);
4778 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
4779
4780 view.update(cx, |view, cx| {
4781 view.set_wrap_width(Some(140.), cx);
4782 assert_eq!(
4783 view.display_text(cx),
4784 "use one::{\n two::three::\n four::five\n};"
4785 );
4786
4787 view.select_display_ranges(&[DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)], cx);
4788
4789 view.move_to_next_word_boundary(&MoveToNextWordBoundary, cx);
4790 assert_eq!(
4791 view.selected_display_ranges(cx),
4792 &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
4793 );
4794
4795 view.move_to_next_word_boundary(&MoveToNextWordBoundary, cx);
4796 assert_eq!(
4797 view.selected_display_ranges(cx),
4798 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
4799 );
4800
4801 view.move_to_next_word_boundary(&MoveToNextWordBoundary, cx);
4802 assert_eq!(
4803 view.selected_display_ranges(cx),
4804 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
4805 );
4806
4807 view.move_to_next_word_boundary(&MoveToNextWordBoundary, cx);
4808 assert_eq!(
4809 view.selected_display_ranges(cx),
4810 &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
4811 );
4812
4813 view.move_to_previous_word_boundary(&MoveToPreviousWordBoundary, cx);
4814 assert_eq!(
4815 view.selected_display_ranges(cx),
4816 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
4817 );
4818
4819 view.move_to_previous_word_boundary(&MoveToPreviousWordBoundary, cx);
4820 assert_eq!(
4821 view.selected_display_ranges(cx),
4822 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
4823 );
4824 });
4825 }
4826
4827 #[gpui::test]
4828 fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
4829 let buffer = MultiBuffer::build_simple("one two three four", cx);
4830 let settings = EditorSettings::test(&cx);
4831 let (_, view) = cx.add_window(Default::default(), |cx| {
4832 build_editor(buffer.clone(), settings, cx)
4833 });
4834
4835 view.update(cx, |view, cx| {
4836 view.select_display_ranges(
4837 &[
4838 // an empty selection - the preceding word fragment is deleted
4839 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
4840 // characters selected - they are deleted
4841 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
4842 ],
4843 cx,
4844 );
4845 view.delete_to_previous_word_boundary(&DeleteToPreviousWordBoundary, cx);
4846 });
4847
4848 assert_eq!(buffer.read(cx).read(cx).text(), "e two te four");
4849
4850 view.update(cx, |view, cx| {
4851 view.select_display_ranges(
4852 &[
4853 // an empty selection - the following word fragment is deleted
4854 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
4855 // characters selected - they are deleted
4856 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
4857 ],
4858 cx,
4859 );
4860 view.delete_to_next_word_boundary(&DeleteToNextWordBoundary, cx);
4861 });
4862
4863 assert_eq!(buffer.read(cx).read(cx).text(), "e t te our");
4864 }
4865
4866 #[gpui::test]
4867 fn test_newline(cx: &mut gpui::MutableAppContext) {
4868 let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
4869 let settings = EditorSettings::test(&cx);
4870 let (_, view) = cx.add_window(Default::default(), |cx| {
4871 build_editor(buffer.clone(), settings, cx)
4872 });
4873
4874 view.update(cx, |view, cx| {
4875 view.select_display_ranges(
4876 &[
4877 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
4878 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
4879 DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
4880 ],
4881 cx,
4882 );
4883
4884 view.newline(&Newline, cx);
4885 assert_eq!(view.text(cx), "aa\naa\n \n bb\n bb\n");
4886 });
4887 }
4888
4889 #[gpui::test]
4890 fn test_indent_outdent(cx: &mut gpui::MutableAppContext) {
4891 let buffer = MultiBuffer::build_simple(" one two\nthree\n four", cx);
4892 let settings = EditorSettings::test(&cx);
4893 let (_, view) = cx.add_window(Default::default(), |cx| {
4894 build_editor(buffer.clone(), settings, cx)
4895 });
4896
4897 view.update(cx, |view, cx| {
4898 // two selections on the same line
4899 view.select_display_ranges(
4900 &[
4901 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 5),
4902 DisplayPoint::new(0, 6)..DisplayPoint::new(0, 9),
4903 ],
4904 cx,
4905 );
4906
4907 // indent from mid-tabstop to full tabstop
4908 view.tab(&Tab, 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, 4)..DisplayPoint::new(0, 7),
4914 DisplayPoint::new(0, 8)..DisplayPoint::new(0, 11),
4915 ]
4916 );
4917
4918 // outdent from 1 tabstop to 0 tabstops
4919 view.outdent(&Outdent, cx);
4920 assert_eq!(view.text(cx), "one two\nthree\n four");
4921 assert_eq!(
4922 view.selected_display_ranges(cx),
4923 &[
4924 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 3),
4925 DisplayPoint::new(0, 4)..DisplayPoint::new(0, 7),
4926 ]
4927 );
4928
4929 // select across line ending
4930 view.select_display_ranges(&[DisplayPoint::new(1, 1)..DisplayPoint::new(2, 0)], cx);
4931
4932 // indent and outdent affect only the preceding line
4933 view.tab(&Tab, cx);
4934 assert_eq!(view.text(cx), "one two\n three\n four");
4935 assert_eq!(
4936 view.selected_display_ranges(cx),
4937 &[DisplayPoint::new(1, 5)..DisplayPoint::new(2, 0)]
4938 );
4939 view.outdent(&Outdent, cx);
4940 assert_eq!(view.text(cx), "one two\nthree\n four");
4941 assert_eq!(
4942 view.selected_display_ranges(cx),
4943 &[DisplayPoint::new(1, 1)..DisplayPoint::new(2, 0)]
4944 );
4945
4946 // Ensure that indenting/outdenting works when the cursor is at column 0.
4947 view.select_display_ranges(&[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)], cx);
4948 view.tab(&Tab, cx);
4949 assert_eq!(view.text(cx), "one two\n three\n four");
4950 assert_eq!(
4951 view.selected_display_ranges(cx),
4952 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
4953 );
4954
4955 view.select_display_ranges(&[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)], cx);
4956 view.outdent(&Outdent, cx);
4957 assert_eq!(view.text(cx), "one two\nthree\n four");
4958 assert_eq!(
4959 view.selected_display_ranges(cx),
4960 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
4961 );
4962 });
4963 }
4964
4965 #[gpui::test]
4966 fn test_backspace(cx: &mut gpui::MutableAppContext) {
4967 let buffer =
4968 MultiBuffer::build_simple("one two three\nfour five six\nseven eight nine\nten\n", cx);
4969 let settings = EditorSettings::test(&cx);
4970 let (_, view) = cx.add_window(Default::default(), |cx| {
4971 build_editor(buffer.clone(), settings, cx)
4972 });
4973
4974 view.update(cx, |view, cx| {
4975 view.select_display_ranges(
4976 &[
4977 // an empty selection - the preceding character is deleted
4978 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
4979 // one character selected - it is deleted
4980 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
4981 // a line suffix selected - it is deleted
4982 DisplayPoint::new(2, 6)..DisplayPoint::new(3, 0),
4983 ],
4984 cx,
4985 );
4986 view.backspace(&Backspace, cx);
4987 });
4988
4989 assert_eq!(
4990 buffer.read(cx).read(cx).text(),
4991 "oe two three\nfou five six\nseven ten\n"
4992 );
4993 }
4994
4995 #[gpui::test]
4996 fn test_delete(cx: &mut gpui::MutableAppContext) {
4997 let buffer =
4998 MultiBuffer::build_simple("one two three\nfour five six\nseven eight nine\nten\n", cx);
4999 let settings = EditorSettings::test(&cx);
5000 let (_, view) = cx.add_window(Default::default(), |cx| {
5001 build_editor(buffer.clone(), settings, cx)
5002 });
5003
5004 view.update(cx, |view, cx| {
5005 view.select_display_ranges(
5006 &[
5007 // an empty selection - the following character is deleted
5008 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
5009 // one character selected - it is deleted
5010 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
5011 // a line suffix selected - it is deleted
5012 DisplayPoint::new(2, 6)..DisplayPoint::new(3, 0),
5013 ],
5014 cx,
5015 );
5016 view.delete(&Delete, cx);
5017 });
5018
5019 assert_eq!(
5020 buffer.read(cx).read(cx).text(),
5021 "on two three\nfou five six\nseven ten\n"
5022 );
5023 }
5024
5025 #[gpui::test]
5026 fn test_delete_line(cx: &mut gpui::MutableAppContext) {
5027 let settings = EditorSettings::test(&cx);
5028 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
5029 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
5030 view.update(cx, |view, cx| {
5031 view.select_display_ranges(
5032 &[
5033 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
5034 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
5035 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
5036 ],
5037 cx,
5038 );
5039 view.delete_line(&DeleteLine, cx);
5040 assert_eq!(view.display_text(cx), "ghi");
5041 assert_eq!(
5042 view.selected_display_ranges(cx),
5043 vec![
5044 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
5045 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
5046 ]
5047 );
5048 });
5049
5050 let settings = EditorSettings::test(&cx);
5051 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
5052 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
5053 view.update(cx, |view, cx| {
5054 view.select_display_ranges(&[DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)], cx);
5055 view.delete_line(&DeleteLine, cx);
5056 assert_eq!(view.display_text(cx), "ghi\n");
5057 assert_eq!(
5058 view.selected_display_ranges(cx),
5059 vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
5060 );
5061 });
5062 }
5063
5064 #[gpui::test]
5065 fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
5066 let settings = EditorSettings::test(&cx);
5067 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
5068 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
5069 view.update(cx, |view, cx| {
5070 view.select_display_ranges(
5071 &[
5072 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
5073 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
5074 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
5075 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
5076 ],
5077 cx,
5078 );
5079 view.duplicate_line(&DuplicateLine, cx);
5080 assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
5081 assert_eq!(
5082 view.selected_display_ranges(cx),
5083 vec![
5084 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
5085 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
5086 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
5087 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
5088 ]
5089 );
5090 });
5091
5092 let settings = EditorSettings::test(&cx);
5093 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
5094 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
5095 view.update(cx, |view, cx| {
5096 view.select_display_ranges(
5097 &[
5098 DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
5099 DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
5100 ],
5101 cx,
5102 );
5103 view.duplicate_line(&DuplicateLine, cx);
5104 assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
5105 assert_eq!(
5106 view.selected_display_ranges(cx),
5107 vec![
5108 DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
5109 DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
5110 ]
5111 );
5112 });
5113 }
5114
5115 #[gpui::test]
5116 fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
5117 let settings = EditorSettings::test(&cx);
5118 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
5119 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
5120 view.update(cx, |view, cx| {
5121 view.fold_ranges(
5122 vec![
5123 Point::new(0, 2)..Point::new(1, 2),
5124 Point::new(2, 3)..Point::new(4, 1),
5125 Point::new(7, 0)..Point::new(8, 4),
5126 ],
5127 cx,
5128 );
5129 view.select_display_ranges(
5130 &[
5131 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
5132 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
5133 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
5134 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
5135 ],
5136 cx,
5137 );
5138 assert_eq!(
5139 view.display_text(cx),
5140 "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
5141 );
5142
5143 view.move_line_up(&MoveLineUp, cx);
5144 assert_eq!(
5145 view.display_text(cx),
5146 "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
5147 );
5148 assert_eq!(
5149 view.selected_display_ranges(cx),
5150 vec![
5151 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
5152 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
5153 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
5154 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
5155 ]
5156 );
5157 });
5158
5159 view.update(cx, |view, cx| {
5160 view.move_line_down(&MoveLineDown, cx);
5161 assert_eq!(
5162 view.display_text(cx),
5163 "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
5164 );
5165 assert_eq!(
5166 view.selected_display_ranges(cx),
5167 vec![
5168 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
5169 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
5170 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
5171 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
5172 ]
5173 );
5174 });
5175
5176 view.update(cx, |view, cx| {
5177 view.move_line_down(&MoveLineDown, cx);
5178 assert_eq!(
5179 view.display_text(cx),
5180 "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
5181 );
5182 assert_eq!(
5183 view.selected_display_ranges(cx),
5184 vec![
5185 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
5186 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
5187 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
5188 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
5189 ]
5190 );
5191 });
5192
5193 view.update(cx, |view, cx| {
5194 view.move_line_up(&MoveLineUp, cx);
5195 assert_eq!(
5196 view.display_text(cx),
5197 "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
5198 );
5199 assert_eq!(
5200 view.selected_display_ranges(cx),
5201 vec![
5202 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
5203 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
5204 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
5205 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
5206 ]
5207 );
5208 });
5209 }
5210
5211 #[gpui::test]
5212 fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
5213 let settings = EditorSettings::test(&cx);
5214 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
5215 let snapshot = buffer.read(cx).snapshot(cx);
5216 let (_, editor) =
5217 cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
5218 editor.update(cx, |editor, cx| {
5219 editor.insert_blocks(
5220 [BlockProperties {
5221 position: snapshot.anchor_after(Point::new(2, 0)),
5222 disposition: BlockDisposition::Below,
5223 height: 1,
5224 render: Arc::new(|_| Empty::new().boxed()),
5225 }],
5226 cx,
5227 );
5228 editor.select_ranges([Point::new(2, 0)..Point::new(2, 0)], None, cx);
5229 editor.move_line_down(&MoveLineDown, cx);
5230 });
5231 }
5232
5233 #[gpui::test]
5234 fn test_clipboard(cx: &mut gpui::MutableAppContext) {
5235 let buffer = MultiBuffer::build_simple("one✅ two three four five six ", cx);
5236 let settings = EditorSettings::test(&cx);
5237 let view = cx
5238 .add_window(Default::default(), |cx| {
5239 build_editor(buffer.clone(), settings, cx)
5240 })
5241 .1;
5242
5243 // Cut with three selections. Clipboard text is divided into three slices.
5244 view.update(cx, |view, cx| {
5245 view.select_ranges(vec![0..7, 11..17, 22..27], None, cx);
5246 view.cut(&Cut, cx);
5247 assert_eq!(view.display_text(cx), "two four six ");
5248 });
5249
5250 // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
5251 view.update(cx, |view, cx| {
5252 view.select_ranges(vec![4..4, 9..9, 13..13], None, cx);
5253 view.paste(&Paste, cx);
5254 assert_eq!(view.display_text(cx), "two one✅ four three six five ");
5255 assert_eq!(
5256 view.selected_display_ranges(cx),
5257 &[
5258 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
5259 DisplayPoint::new(0, 22)..DisplayPoint::new(0, 22),
5260 DisplayPoint::new(0, 31)..DisplayPoint::new(0, 31)
5261 ]
5262 );
5263 });
5264
5265 // Paste again but with only two cursors. Since the number of cursors doesn't
5266 // match the number of slices in the clipboard, the entire clipboard text
5267 // is pasted at each cursor.
5268 view.update(cx, |view, cx| {
5269 view.select_ranges(vec![0..0, 31..31], None, cx);
5270 view.handle_input(&Input("( ".into()), cx);
5271 view.paste(&Paste, cx);
5272 view.handle_input(&Input(") ".into()), cx);
5273 assert_eq!(
5274 view.display_text(cx),
5275 "( one✅ three five ) two one✅ four three six five ( one✅ three five ) "
5276 );
5277 });
5278
5279 view.update(cx, |view, cx| {
5280 view.select_ranges(vec![0..0], None, cx);
5281 view.handle_input(&Input("123\n4567\n89\n".into()), cx);
5282 assert_eq!(
5283 view.display_text(cx),
5284 "123\n4567\n89\n( one✅ three five ) two one✅ four three six five ( one✅ three five ) "
5285 );
5286 });
5287
5288 // Cut with three selections, one of which is full-line.
5289 view.update(cx, |view, cx| {
5290 view.select_display_ranges(
5291 &[
5292 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2),
5293 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
5294 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
5295 ],
5296 cx,
5297 );
5298 view.cut(&Cut, cx);
5299 assert_eq!(
5300 view.display_text(cx),
5301 "13\n9\n( one✅ three five ) two one✅ four three six five ( one✅ three five ) "
5302 );
5303 });
5304
5305 // Paste with three selections, noticing how the copied selection that was full-line
5306 // gets inserted before the second cursor.
5307 view.update(cx, |view, cx| {
5308 view.select_display_ranges(
5309 &[
5310 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
5311 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
5312 DisplayPoint::new(2, 2)..DisplayPoint::new(2, 3),
5313 ],
5314 cx,
5315 );
5316 view.paste(&Paste, cx);
5317 assert_eq!(
5318 view.display_text(cx),
5319 "123\n4567\n9\n( 8ne✅ three five ) two one✅ four three six five ( one✅ three five ) "
5320 );
5321 assert_eq!(
5322 view.selected_display_ranges(cx),
5323 &[
5324 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
5325 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
5326 DisplayPoint::new(3, 3)..DisplayPoint::new(3, 3),
5327 ]
5328 );
5329 });
5330
5331 // Copy with a single cursor only, which writes the whole line into the clipboard.
5332 view.update(cx, |view, cx| {
5333 view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)], cx);
5334 view.copy(&Copy, cx);
5335 });
5336
5337 // Paste with three selections, noticing how the copied full-line selection is inserted
5338 // before the empty selections but replaces the selection that is non-empty.
5339 view.update(cx, |view, cx| {
5340 view.select_display_ranges(
5341 &[
5342 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
5343 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 2),
5344 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
5345 ],
5346 cx,
5347 );
5348 view.paste(&Paste, cx);
5349 assert_eq!(
5350 view.display_text(cx),
5351 "123\n123\n123\n67\n123\n9\n( 8ne✅ three five ) two one✅ four three six five ( one✅ three five ) "
5352 );
5353 assert_eq!(
5354 view.selected_display_ranges(cx),
5355 &[
5356 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
5357 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
5358 DisplayPoint::new(5, 1)..DisplayPoint::new(5, 1),
5359 ]
5360 );
5361 });
5362 }
5363
5364 #[gpui::test]
5365 fn test_select_all(cx: &mut gpui::MutableAppContext) {
5366 let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
5367 let settings = EditorSettings::test(&cx);
5368 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
5369 view.update(cx, |view, cx| {
5370 view.select_all(&SelectAll, cx);
5371 assert_eq!(
5372 view.selected_display_ranges(cx),
5373 &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
5374 );
5375 });
5376 }
5377
5378 #[gpui::test]
5379 fn test_select_line(cx: &mut gpui::MutableAppContext) {
5380 let settings = EditorSettings::test(&cx);
5381 let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
5382 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
5383 view.update(cx, |view, cx| {
5384 view.select_display_ranges(
5385 &[
5386 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
5387 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
5388 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
5389 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
5390 ],
5391 cx,
5392 );
5393 view.select_line(&SelectLine, cx);
5394 assert_eq!(
5395 view.selected_display_ranges(cx),
5396 vec![
5397 DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
5398 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
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![
5408 DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
5409 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
5410 ]
5411 );
5412 });
5413
5414 view.update(cx, |view, cx| {
5415 view.select_line(&SelectLine, cx);
5416 assert_eq!(
5417 view.selected_display_ranges(cx),
5418 vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
5419 );
5420 });
5421 }
5422
5423 #[gpui::test]
5424 fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
5425 let settings = EditorSettings::test(&cx);
5426 let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
5427 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
5428 view.update(cx, |view, cx| {
5429 view.fold_ranges(
5430 vec![
5431 Point::new(0, 2)..Point::new(1, 2),
5432 Point::new(2, 3)..Point::new(4, 1),
5433 Point::new(7, 0)..Point::new(8, 4),
5434 ],
5435 cx,
5436 );
5437 view.select_display_ranges(
5438 &[
5439 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
5440 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
5441 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
5442 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
5443 ],
5444 cx,
5445 );
5446 assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i");
5447 });
5448
5449 view.update(cx, |view, cx| {
5450 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
5451 assert_eq!(
5452 view.display_text(cx),
5453 "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i"
5454 );
5455 assert_eq!(
5456 view.selected_display_ranges(cx),
5457 [
5458 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
5459 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
5460 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
5461 DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
5462 ]
5463 );
5464 });
5465
5466 view.update(cx, |view, cx| {
5467 view.select_display_ranges(&[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)], cx);
5468 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
5469 assert_eq!(
5470 view.display_text(cx),
5471 "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
5472 );
5473 assert_eq!(
5474 view.selected_display_ranges(cx),
5475 [
5476 DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
5477 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
5478 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
5479 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
5480 DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
5481 DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
5482 DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
5483 DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
5484 ]
5485 );
5486 });
5487 }
5488
5489 #[gpui::test]
5490 fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
5491 let settings = EditorSettings::test(&cx);
5492 let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
5493 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, settings, cx));
5494
5495 view.update(cx, |view, cx| {
5496 view.select_display_ranges(&[DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)], cx);
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_above(&AddSelectionAbove, cx);
5511 assert_eq!(
5512 view.selected_display_ranges(cx),
5513 vec![
5514 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
5515 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
5516 ]
5517 );
5518 });
5519
5520 view.update(cx, |view, cx| {
5521 view.add_selection_below(&AddSelectionBelow, cx);
5522 assert_eq!(
5523 view.selected_display_ranges(cx),
5524 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
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.add_selection_below(&AddSelectionBelow, cx);
5541 assert_eq!(
5542 view.selected_display_ranges(cx),
5543 vec![
5544 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
5545 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
5546 ]
5547 );
5548 });
5549
5550 view.update(cx, |view, cx| {
5551 view.select_display_ranges(&[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)], cx);
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_below(&AddSelectionBelow, cx);
5566 assert_eq!(
5567 view.selected_display_ranges(cx),
5568 vec![
5569 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
5570 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
5571 ]
5572 );
5573 });
5574
5575 view.update(cx, |view, cx| {
5576 view.add_selection_above(&AddSelectionAbove, cx);
5577 assert_eq!(
5578 view.selected_display_ranges(cx),
5579 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
5580 );
5581 });
5582
5583 view.update(cx, |view, cx| {
5584 view.add_selection_above(&AddSelectionAbove, cx);
5585 assert_eq!(
5586 view.selected_display_ranges(cx),
5587 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
5588 );
5589 });
5590
5591 view.update(cx, |view, cx| {
5592 view.select_display_ranges(&[DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)], cx);
5593 view.add_selection_below(&AddSelectionBelow, cx);
5594 assert_eq!(
5595 view.selected_display_ranges(cx),
5596 vec![
5597 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
5598 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
5599 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
5600 ]
5601 );
5602 });
5603
5604 view.update(cx, |view, cx| {
5605 view.add_selection_below(&AddSelectionBelow, cx);
5606 assert_eq!(
5607 view.selected_display_ranges(cx),
5608 vec![
5609 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
5610 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
5611 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
5612 DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
5613 ]
5614 );
5615 });
5616
5617 view.update(cx, |view, cx| {
5618 view.add_selection_above(&AddSelectionAbove, cx);
5619 assert_eq!(
5620 view.selected_display_ranges(cx),
5621 vec![
5622 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
5623 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
5624 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
5625 ]
5626 );
5627 });
5628
5629 view.update(cx, |view, cx| {
5630 view.select_display_ranges(&[DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)], cx);
5631 });
5632 view.update(cx, |view, cx| {
5633 view.add_selection_above(&AddSelectionAbove, cx);
5634 assert_eq!(
5635 view.selected_display_ranges(cx),
5636 vec![
5637 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
5638 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
5639 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
5640 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
5641 ]
5642 );
5643 });
5644
5645 view.update(cx, |view, cx| {
5646 view.add_selection_below(&AddSelectionBelow, cx);
5647 assert_eq!(
5648 view.selected_display_ranges(cx),
5649 vec![
5650 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
5651 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
5652 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
5653 ]
5654 );
5655 });
5656 }
5657
5658 #[gpui::test]
5659 async fn test_select_larger_smaller_syntax_node(mut cx: gpui::TestAppContext) {
5660 let settings = cx.read(EditorSettings::test);
5661 let language = Some(Arc::new(Language::new(
5662 LanguageConfig::default(),
5663 Some(tree_sitter_rust::language()),
5664 )));
5665
5666 let text = r#"
5667 use mod1::mod2::{mod3, mod4};
5668
5669 fn fn_1(param1: bool, param2: &str) {
5670 let var1 = "text";
5671 }
5672 "#
5673 .unindent();
5674
5675 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, None, cx));
5676 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
5677 let (_, view) = cx.add_window(|cx| build_editor(buffer, settings, cx));
5678 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5679 .await;
5680
5681 view.update(&mut cx, |view, cx| {
5682 view.select_display_ranges(
5683 &[
5684 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
5685 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
5686 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
5687 ],
5688 cx,
5689 );
5690 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
5691 });
5692 assert_eq!(
5693 view.update(&mut cx, |view, cx| view.selected_display_ranges(cx)),
5694 &[
5695 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
5696 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
5697 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
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 &[
5707 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
5708 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
5709 ]
5710 );
5711
5712 view.update(&mut cx, |view, cx| {
5713 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
5714 });
5715 assert_eq!(
5716 view.update(&mut cx, |view, cx| view.selected_display_ranges(cx)),
5717 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
5718 );
5719
5720 // Trying to expand the selected syntax node one more time has no effect.
5721 view.update(&mut cx, |view, cx| {
5722 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
5723 });
5724 assert_eq!(
5725 view.update(&mut cx, |view, cx| view.selected_display_ranges(cx)),
5726 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
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, 16)..DisplayPoint::new(0, 28),
5736 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
5737 ]
5738 );
5739
5740 view.update(&mut cx, |view, cx| {
5741 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
5742 });
5743 assert_eq!(
5744 view.update(&mut cx, |view, cx| view.selected_display_ranges(cx)),
5745 &[
5746 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
5747 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
5748 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
5749 ]
5750 );
5751
5752 view.update(&mut cx, |view, cx| {
5753 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
5754 });
5755 assert_eq!(
5756 view.update(&mut cx, |view, cx| view.selected_display_ranges(cx)),
5757 &[
5758 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
5759 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
5760 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
5761 ]
5762 );
5763
5764 // Trying to shrink the selected syntax node one more time has no effect.
5765 view.update(&mut cx, |view, cx| {
5766 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
5767 });
5768 assert_eq!(
5769 view.update(&mut cx, |view, cx| view.selected_display_ranges(cx)),
5770 &[
5771 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
5772 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
5773 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
5774 ]
5775 );
5776
5777 // Ensure that we keep expanding the selection if the larger selection starts or ends within
5778 // a fold.
5779 view.update(&mut cx, |view, cx| {
5780 view.fold_ranges(
5781 vec![
5782 Point::new(0, 21)..Point::new(0, 24),
5783 Point::new(3, 20)..Point::new(3, 22),
5784 ],
5785 cx,
5786 );
5787 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
5788 });
5789 assert_eq!(
5790 view.update(&mut cx, |view, cx| view.selected_display_ranges(cx)),
5791 &[
5792 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
5793 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
5794 DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
5795 ]
5796 );
5797 }
5798
5799 #[gpui::test]
5800 async fn test_autoindent_selections(mut cx: gpui::TestAppContext) {
5801 let settings = cx.read(EditorSettings::test);
5802 let language = Some(Arc::new(
5803 Language::new(
5804 LanguageConfig {
5805 brackets: vec![
5806 BracketPair {
5807 start: "{".to_string(),
5808 end: "}".to_string(),
5809 close: false,
5810 newline: true,
5811 },
5812 BracketPair {
5813 start: "(".to_string(),
5814 end: ")".to_string(),
5815 close: false,
5816 newline: true,
5817 },
5818 ],
5819 ..Default::default()
5820 },
5821 Some(tree_sitter_rust::language()),
5822 )
5823 .with_indents_query(
5824 r#"
5825 (_ "(" ")" @end) @indent
5826 (_ "{" "}" @end) @indent
5827 "#,
5828 )
5829 .unwrap(),
5830 ));
5831
5832 let text = "fn a() {}";
5833
5834 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, None, cx));
5835 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
5836 let (_, editor) = cx.add_window(|cx| build_editor(buffer, settings, cx));
5837 editor
5838 .condition(&cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
5839 .await;
5840
5841 editor.update(&mut cx, |editor, cx| {
5842 editor.select_ranges([5..5, 8..8, 9..9], None, cx);
5843 editor.newline(&Newline, cx);
5844 assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
5845 assert_eq!(
5846 editor.selected_ranges(cx),
5847 &[
5848 Point::new(1, 4)..Point::new(1, 4),
5849 Point::new(3, 4)..Point::new(3, 4),
5850 Point::new(5, 0)..Point::new(5, 0)
5851 ]
5852 );
5853 });
5854 }
5855
5856 #[gpui::test]
5857 async fn test_autoclose_pairs(mut cx: gpui::TestAppContext) {
5858 let settings = cx.read(EditorSettings::test);
5859 let language = Some(Arc::new(Language::new(
5860 LanguageConfig {
5861 brackets: vec![
5862 BracketPair {
5863 start: "{".to_string(),
5864 end: "}".to_string(),
5865 close: true,
5866 newline: true,
5867 },
5868 BracketPair {
5869 start: "/*".to_string(),
5870 end: " */".to_string(),
5871 close: true,
5872 newline: true,
5873 },
5874 ],
5875 ..Default::default()
5876 },
5877 Some(tree_sitter_rust::language()),
5878 )));
5879
5880 let text = r#"
5881 a
5882
5883 /
5884
5885 "#
5886 .unindent();
5887
5888 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, None, cx));
5889 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
5890 let (_, view) = cx.add_window(|cx| build_editor(buffer, settings, cx));
5891 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
5892 .await;
5893
5894 view.update(&mut cx, |view, cx| {
5895 view.select_display_ranges(
5896 &[
5897 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
5898 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
5899 ],
5900 cx,
5901 );
5902 view.handle_input(&Input("{".to_string()), cx);
5903 view.handle_input(&Input("{".to_string()), cx);
5904 view.handle_input(&Input("{".to_string()), cx);
5905 assert_eq!(
5906 view.text(cx),
5907 "
5908 {{{}}}
5909 {{{}}}
5910 /
5911
5912 "
5913 .unindent()
5914 );
5915
5916 view.move_right(&MoveRight, cx);
5917 view.handle_input(&Input("}".to_string()), cx);
5918 view.handle_input(&Input("}".to_string()), cx);
5919 view.handle_input(&Input("}".to_string()), cx);
5920 assert_eq!(
5921 view.text(cx),
5922 "
5923 {{{}}}}
5924 {{{}}}}
5925 /
5926
5927 "
5928 .unindent()
5929 );
5930
5931 view.undo(&Undo, cx);
5932 view.handle_input(&Input("/".to_string()), cx);
5933 view.handle_input(&Input("*".to_string()), cx);
5934 assert_eq!(
5935 view.text(cx),
5936 "
5937 /* */
5938 /* */
5939 /
5940
5941 "
5942 .unindent()
5943 );
5944
5945 view.undo(&Undo, cx);
5946 view.select_display_ranges(
5947 &[
5948 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
5949 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
5950 ],
5951 cx,
5952 );
5953 view.handle_input(&Input("*".to_string()), cx);
5954 assert_eq!(
5955 view.text(cx),
5956 "
5957 a
5958
5959 /*
5960 *
5961 "
5962 .unindent()
5963 );
5964 });
5965 }
5966
5967 #[gpui::test]
5968 async fn test_toggle_comment(mut cx: gpui::TestAppContext) {
5969 let settings = cx.read(EditorSettings::test);
5970 let language = Some(Arc::new(Language::new(
5971 LanguageConfig {
5972 line_comment: Some("// ".to_string()),
5973 ..Default::default()
5974 },
5975 Some(tree_sitter_rust::language()),
5976 )));
5977
5978 let text = "
5979 fn a() {
5980 //b();
5981 // c();
5982 // d();
5983 }
5984 "
5985 .unindent();
5986
5987 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, None, cx));
5988 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
5989 let (_, view) = cx.add_window(|cx| build_editor(buffer, settings, cx));
5990
5991 view.update(&mut cx, |editor, cx| {
5992 // If multiple selections intersect a line, the line is only
5993 // toggled once.
5994 editor.select_display_ranges(
5995 &[
5996 DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
5997 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
5998 ],
5999 cx,
6000 );
6001 editor.toggle_comments(&ToggleComments, cx);
6002 assert_eq!(
6003 editor.text(cx),
6004 "
6005 fn a() {
6006 b();
6007 c();
6008 d();
6009 }
6010 "
6011 .unindent()
6012 );
6013
6014 // The comment prefix is inserted at the same column for every line
6015 // in a selection.
6016 editor.select_display_ranges(&[DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)], cx);
6017 editor.toggle_comments(&ToggleComments, cx);
6018 assert_eq!(
6019 editor.text(cx),
6020 "
6021 fn a() {
6022 // b();
6023 // c();
6024 // d();
6025 }
6026 "
6027 .unindent()
6028 );
6029
6030 // If a selection ends at the beginning of a line, that line is not toggled.
6031 editor.select_display_ranges(&[DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)], cx);
6032 editor.toggle_comments(&ToggleComments, cx);
6033 assert_eq!(
6034 editor.text(cx),
6035 "
6036 fn a() {
6037 // b();
6038 c();
6039 // d();
6040 }
6041 "
6042 .unindent()
6043 );
6044 });
6045 }
6046
6047 #[gpui::test]
6048 fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
6049 let settings = EditorSettings::test(cx);
6050 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
6051 let multibuffer = cx.add_model(|cx| {
6052 let mut multibuffer = MultiBuffer::new(0);
6053 multibuffer.push_excerpt(
6054 ExcerptProperties {
6055 buffer: &buffer,
6056 range: Point::new(0, 0)..Point::new(0, 4),
6057 },
6058 cx,
6059 );
6060 multibuffer.push_excerpt(
6061 ExcerptProperties {
6062 buffer: &buffer,
6063 range: Point::new(1, 0)..Point::new(1, 4),
6064 },
6065 cx,
6066 );
6067 multibuffer
6068 });
6069
6070 assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
6071
6072 let (_, view) = cx.add_window(Default::default(), |cx| {
6073 build_editor(multibuffer, settings, cx)
6074 });
6075 view.update(cx, |view, cx| {
6076 view.select_display_ranges(
6077 &[
6078 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
6079 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
6080 ],
6081 cx,
6082 );
6083
6084 view.handle_input(&Input("X".to_string()), cx);
6085 assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
6086 assert_eq!(
6087 view.selected_display_ranges(cx),
6088 &[
6089 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
6090 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
6091 ]
6092 )
6093 });
6094 }
6095
6096 #[gpui::test]
6097 fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
6098 let settings = EditorSettings::test(cx);
6099 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
6100 let multibuffer = cx.add_model(|cx| {
6101 let mut multibuffer = MultiBuffer::new(0);
6102 multibuffer.push_excerpt(
6103 ExcerptProperties {
6104 buffer: &buffer,
6105 range: Point::new(0, 0)..Point::new(1, 4),
6106 },
6107 cx,
6108 );
6109 multibuffer.push_excerpt(
6110 ExcerptProperties {
6111 buffer: &buffer,
6112 range: Point::new(1, 0)..Point::new(2, 4),
6113 },
6114 cx,
6115 );
6116 multibuffer
6117 });
6118
6119 assert_eq!(
6120 multibuffer.read(cx).read(cx).text(),
6121 "aaaa\nbbbb\nbbbb\ncccc"
6122 );
6123
6124 let (_, view) = cx.add_window(Default::default(), |cx| {
6125 build_editor(multibuffer, settings, cx)
6126 });
6127 view.update(cx, |view, cx| {
6128 view.select_display_ranges(
6129 &[
6130 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
6131 DisplayPoint::new(2, 3)..DisplayPoint::new(2, 3),
6132 ],
6133 cx,
6134 );
6135
6136 view.handle_input(&Input("X".to_string()), cx);
6137 assert_eq!(view.text(cx), "aaaa\nbXbbXb\nbXbbXb\ncccc");
6138 assert_eq!(
6139 view.selected_display_ranges(cx),
6140 &[
6141 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
6142 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
6143 ]
6144 );
6145
6146 view.newline(&Newline, cx);
6147 assert_eq!(view.text(cx), "aaaa\nbX\nbbX\nb\nbX\nbbX\nb\ncccc");
6148 assert_eq!(
6149 view.selected_display_ranges(cx),
6150 &[
6151 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
6152 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
6153 ]
6154 );
6155 });
6156 }
6157
6158 #[gpui::test]
6159 fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
6160 let settings = EditorSettings::test(cx);
6161 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
6162 let mut excerpt1_id = None;
6163 let multibuffer = cx.add_model(|cx| {
6164 let mut multibuffer = MultiBuffer::new(0);
6165 excerpt1_id = Some(multibuffer.push_excerpt(
6166 ExcerptProperties {
6167 buffer: &buffer,
6168 range: Point::new(0, 0)..Point::new(1, 4),
6169 },
6170 cx,
6171 ));
6172 multibuffer.push_excerpt(
6173 ExcerptProperties {
6174 buffer: &buffer,
6175 range: Point::new(1, 0)..Point::new(2, 4),
6176 },
6177 cx,
6178 );
6179 multibuffer
6180 });
6181 assert_eq!(
6182 multibuffer.read(cx).read(cx).text(),
6183 "aaaa\nbbbb\nbbbb\ncccc"
6184 );
6185 let (_, editor) = cx.add_window(Default::default(), |cx| {
6186 let mut editor = build_editor(multibuffer.clone(), settings, cx);
6187 editor.select_display_ranges(
6188 &[
6189 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
6190 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
6191 ],
6192 cx,
6193 );
6194 editor
6195 });
6196
6197 // Refreshing selections is a no-op when excerpts haven't changed.
6198 editor.update(cx, |editor, cx| {
6199 editor.refresh_selections(cx);
6200 assert_eq!(
6201 editor.selected_display_ranges(cx),
6202 [
6203 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
6204 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
6205 ]
6206 );
6207 });
6208
6209 multibuffer.update(cx, |multibuffer, cx| {
6210 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
6211 });
6212 editor.update(cx, |editor, cx| {
6213 // Removing an excerpt causes the first selection to become degenerate.
6214 assert_eq!(
6215 editor.selected_display_ranges(cx),
6216 [
6217 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
6218 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
6219 ]
6220 );
6221
6222 // Refreshing selections will relocate the first selection to the original buffer
6223 // location.
6224 editor.refresh_selections(cx);
6225 assert_eq!(
6226 editor.selected_display_ranges(cx),
6227 [
6228 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
6229 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3)
6230 ]
6231 );
6232 });
6233 }
6234
6235 #[gpui::test]
6236 async fn test_extra_newline_insertion(mut cx: gpui::TestAppContext) {
6237 let settings = cx.read(EditorSettings::test);
6238 let language = Some(Arc::new(Language::new(
6239 LanguageConfig {
6240 brackets: vec![
6241 BracketPair {
6242 start: "{".to_string(),
6243 end: "}".to_string(),
6244 close: true,
6245 newline: true,
6246 },
6247 BracketPair {
6248 start: "/* ".to_string(),
6249 end: " */".to_string(),
6250 close: true,
6251 newline: true,
6252 },
6253 ],
6254 ..Default::default()
6255 },
6256 Some(tree_sitter_rust::language()),
6257 )));
6258
6259 let text = concat!(
6260 "{ }\n", // Suppress rustfmt
6261 " x\n", //
6262 " /* */\n", //
6263 "x\n", //
6264 "{{} }\n", //
6265 );
6266
6267 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, None, cx));
6268 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6269 let (_, view) = cx.add_window(|cx| build_editor(buffer, settings, cx));
6270 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
6271 .await;
6272
6273 view.update(&mut cx, |view, cx| {
6274 view.select_display_ranges(
6275 &[
6276 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
6277 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
6278 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
6279 ],
6280 cx,
6281 );
6282 view.newline(&Newline, cx);
6283
6284 assert_eq!(
6285 view.buffer().read(cx).read(cx).text(),
6286 concat!(
6287 "{ \n", // Suppress rustfmt
6288 "\n", //
6289 "}\n", //
6290 " x\n", //
6291 " /* \n", //
6292 " \n", //
6293 " */\n", //
6294 "x\n", //
6295 "{{} \n", //
6296 "}\n", //
6297 )
6298 );
6299 });
6300 }
6301
6302 fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
6303 let point = DisplayPoint::new(row as u32, column as u32);
6304 point..point
6305 }
6306
6307 fn build_editor(
6308 buffer: ModelHandle<MultiBuffer>,
6309 settings: EditorSettings,
6310 cx: &mut ViewContext<Editor>,
6311 ) -> Editor {
6312 Editor::for_buffer(buffer, Arc::new(move |_| settings.clone()), cx)
6313 }
6314}
6315
6316trait RangeExt<T> {
6317 fn sorted(&self) -> Range<T>;
6318 fn to_inclusive(&self) -> RangeInclusive<T>;
6319}
6320
6321impl<T: Ord + Clone> RangeExt<T> for Range<T> {
6322 fn sorted(&self) -> Self {
6323 cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone()
6324 }
6325
6326 fn to_inclusive(&self) -> RangeInclusive<T> {
6327 self.start.clone()..=self.end.clone()
6328 }
6329}