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