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