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