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