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