1pub mod display_map;
2mod element;
3pub mod items;
4pub mod movement;
5mod multi_buffer;
6mod selections_collection;
7
8#[cfg(test)]
9mod test;
10
11use aho_corasick::AhoCorasick;
12use anyhow::Result;
13use clock::ReplicaId;
14use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque};
15pub use display_map::DisplayPoint;
16use display_map::*;
17pub use element::*;
18use fuzzy::{StringMatch, StringMatchCandidate};
19use gpui::{
20 actions,
21 color::Color,
22 elements::*,
23 executor,
24 fonts::{self, HighlightStyle, TextStyle},
25 geometry::vector::{vec2f, Vector2F},
26 impl_actions, impl_internal_actions,
27 platform::CursorStyle,
28 text_layout, AppContext, AsyncAppContext, ClipboardItem, Element, ElementBox, Entity,
29 ModelHandle, MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle,
30 WeakViewHandle,
31};
32pub use language::{char_kind, CharKind};
33use language::{
34 BracketPair, Buffer, CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticSeverity,
35 Language, OffsetRangeExt, Point, Selection, SelectionGoal, TransactionId,
36};
37use multi_buffer::MultiBufferChunks;
38pub use multi_buffer::{
39 Anchor, AnchorRangeExt, ExcerptId, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint,
40};
41use ordered_float::OrderedFloat;
42use project::{Project, ProjectTransaction};
43use selections_collection::{resolve_multiple, MutableSelectionsCollection, SelectionsCollection};
44use serde::{Deserialize, Serialize};
45use settings::Settings;
46use smallvec::SmallVec;
47use smol::Timer;
48use snippet::Snippet;
49use std::{
50 any::TypeId,
51 borrow::Cow,
52 cmp::{self, Ordering, Reverse},
53 iter, mem,
54 ops::{Deref, DerefMut, Range, RangeInclusive, Sub},
55 sync::Arc,
56 time::{Duration, Instant},
57};
58pub use sum_tree::Bias;
59use text::rope::TextDimension;
60use theme::{DiagnosticStyle, Theme};
61use util::{post_inc, ResultExt, TryFutureExt};
62use workspace::{ItemNavHistory, Workspace};
63
64const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
65const MAX_LINE_LEN: usize = 1024;
66const MIN_NAVIGATION_HISTORY_ROW_DELTA: i64 = 10;
67const MAX_SELECTION_HISTORY_LEN: usize = 1024;
68
69#[derive(Clone, Deserialize)]
70pub struct SelectNext {
71 #[serde(default)]
72 pub replace_newest: bool,
73}
74
75#[derive(Clone)]
76pub struct GoToDiagnostic(pub Direction);
77
78#[derive(Clone)]
79pub struct Scroll(pub Vector2F);
80
81#[derive(Clone)]
82pub struct Select(pub SelectPhase);
83
84#[derive(Clone, Deserialize)]
85pub struct Input(pub String);
86
87#[derive(Clone, Deserialize)]
88pub struct SelectToBeginningOfLine {
89 #[serde(default)]
90 stop_at_soft_wraps: bool,
91}
92
93#[derive(Clone, Deserialize)]
94pub struct SelectToEndOfLine {
95 #[serde(default)]
96 stop_at_soft_wraps: bool,
97}
98
99#[derive(Clone, Deserialize)]
100pub struct ToggleCodeActions {
101 #[serde(default)]
102 pub deployed_from_indicator: bool,
103}
104
105#[derive(Clone, Default, Deserialize)]
106pub struct ConfirmCompletion {
107 #[serde(default)]
108 pub item_ix: Option<usize>,
109}
110
111#[derive(Clone, Default, Deserialize)]
112pub struct ConfirmCodeAction {
113 #[serde(default)]
114 pub item_ix: Option<usize>,
115}
116
117actions!(
118 editor,
119 [
120 Cancel,
121 Backspace,
122 Delete,
123 Newline,
124 GoToNextDiagnostic,
125 GoToPrevDiagnostic,
126 Indent,
127 Outdent,
128 DeleteLine,
129 DeleteToPreviousWordStart,
130 DeleteToPreviousSubwordStart,
131 DeleteToNextWordEnd,
132 DeleteToNextSubwordEnd,
133 DeleteToBeginningOfLine,
134 DeleteToEndOfLine,
135 CutToEndOfLine,
136 DuplicateLine,
137 MoveLineUp,
138 MoveLineDown,
139 Transpose,
140 Cut,
141 Copy,
142 Paste,
143 Undo,
144 Redo,
145 MoveUp,
146 MoveDown,
147 MoveLeft,
148 MoveRight,
149 MoveToPreviousWordStart,
150 MoveToPreviousSubwordStart,
151 MoveToNextWordEnd,
152 MoveToNextSubwordEnd,
153 MoveToBeginningOfLine,
154 MoveToEndOfLine,
155 MoveToBeginning,
156 MoveToEnd,
157 SelectUp,
158 SelectDown,
159 SelectLeft,
160 SelectRight,
161 SelectToPreviousWordStart,
162 SelectToPreviousSubwordStart,
163 SelectToNextWordEnd,
164 SelectToNextSubwordEnd,
165 SelectToBeginning,
166 SelectToEnd,
167 SelectAll,
168 SelectLine,
169 SplitSelectionIntoLines,
170 AddSelectionAbove,
171 AddSelectionBelow,
172 Tab,
173 TabPrev,
174 ToggleComments,
175 SelectLargerSyntaxNode,
176 SelectSmallerSyntaxNode,
177 MoveToEnclosingBracket,
178 UndoSelection,
179 RedoSelection,
180 GoToDefinition,
181 FindAllReferences,
182 Rename,
183 ConfirmRename,
184 PageUp,
185 PageDown,
186 Fold,
187 UnfoldLines,
188 FoldSelectedRanges,
189 ShowCompletions,
190 OpenExcerpts,
191 RestartLanguageServer,
192 ]
193);
194
195impl_actions!(
196 editor,
197 [
198 Input,
199 SelectNext,
200 SelectToBeginningOfLine,
201 SelectToEndOfLine,
202 ToggleCodeActions,
203 ConfirmCompletion,
204 ConfirmCodeAction,
205 ]
206);
207
208impl_internal_actions!(editor, [Scroll, Select]);
209
210enum DocumentHighlightRead {}
211enum DocumentHighlightWrite {}
212
213#[derive(Copy, Clone, PartialEq, Eq)]
214pub enum Direction {
215 Prev,
216 Next,
217}
218
219pub fn init(cx: &mut MutableAppContext) {
220 cx.add_action(Editor::open_new);
221 cx.add_action(|this: &mut Editor, action: &Scroll, cx| this.set_scroll_position(action.0, cx));
222 cx.add_action(Editor::select);
223 cx.add_action(Editor::cancel);
224 cx.add_action(Editor::handle_input);
225 cx.add_action(Editor::newline);
226 cx.add_action(Editor::backspace);
227 cx.add_action(Editor::delete);
228 cx.add_action(Editor::tab);
229 cx.add_action(Editor::tab_prev);
230 cx.add_action(Editor::indent);
231 cx.add_action(Editor::outdent);
232 cx.add_action(Editor::delete_line);
233 cx.add_action(Editor::delete_to_previous_word_start);
234 cx.add_action(Editor::delete_to_previous_subword_start);
235 cx.add_action(Editor::delete_to_next_word_end);
236 cx.add_action(Editor::delete_to_next_subword_end);
237 cx.add_action(Editor::delete_to_beginning_of_line);
238 cx.add_action(Editor::delete_to_end_of_line);
239 cx.add_action(Editor::cut_to_end_of_line);
240 cx.add_action(Editor::duplicate_line);
241 cx.add_action(Editor::move_line_up);
242 cx.add_action(Editor::move_line_down);
243 cx.add_action(Editor::transpose);
244 cx.add_action(Editor::cut);
245 cx.add_action(Editor::copy);
246 cx.add_action(Editor::paste);
247 cx.add_action(Editor::undo);
248 cx.add_action(Editor::redo);
249 cx.add_action(Editor::move_up);
250 cx.add_action(Editor::move_down);
251 cx.add_action(Editor::move_left);
252 cx.add_action(Editor::move_right);
253 cx.add_action(Editor::move_to_previous_word_start);
254 cx.add_action(Editor::move_to_previous_subword_start);
255 cx.add_action(Editor::move_to_next_word_end);
256 cx.add_action(Editor::move_to_next_subword_end);
257 cx.add_action(Editor::move_to_beginning_of_line);
258 cx.add_action(Editor::move_to_end_of_line);
259 cx.add_action(Editor::move_to_beginning);
260 cx.add_action(Editor::move_to_end);
261 cx.add_action(Editor::select_up);
262 cx.add_action(Editor::select_down);
263 cx.add_action(Editor::select_left);
264 cx.add_action(Editor::select_right);
265 cx.add_action(Editor::select_to_previous_word_start);
266 cx.add_action(Editor::select_to_previous_subword_start);
267 cx.add_action(Editor::select_to_next_word_end);
268 cx.add_action(Editor::select_to_next_subword_end);
269 cx.add_action(Editor::select_to_beginning_of_line);
270 cx.add_action(Editor::select_to_end_of_line);
271 cx.add_action(Editor::select_to_beginning);
272 cx.add_action(Editor::select_to_end);
273 cx.add_action(Editor::select_all);
274 cx.add_action(Editor::select_line);
275 cx.add_action(Editor::split_selection_into_lines);
276 cx.add_action(Editor::add_selection_above);
277 cx.add_action(Editor::add_selection_below);
278 cx.add_action(Editor::select_next);
279 cx.add_action(Editor::toggle_comments);
280 cx.add_action(Editor::select_larger_syntax_node);
281 cx.add_action(Editor::select_smaller_syntax_node);
282 cx.add_action(Editor::move_to_enclosing_bracket);
283 cx.add_action(Editor::undo_selection);
284 cx.add_action(Editor::redo_selection);
285 cx.add_action(Editor::go_to_next_diagnostic);
286 cx.add_action(Editor::go_to_prev_diagnostic);
287 cx.add_action(Editor::go_to_definition);
288 cx.add_action(Editor::page_up);
289 cx.add_action(Editor::page_down);
290 cx.add_action(Editor::fold);
291 cx.add_action(Editor::unfold_lines);
292 cx.add_action(Editor::fold_selected_ranges);
293 cx.add_action(Editor::show_completions);
294 cx.add_action(Editor::toggle_code_actions);
295 cx.add_action(Editor::open_excerpts);
296 cx.add_action(Editor::restart_language_server);
297 cx.add_async_action(Editor::confirm_completion);
298 cx.add_async_action(Editor::confirm_code_action);
299 cx.add_async_action(Editor::rename);
300 cx.add_async_action(Editor::confirm_rename);
301 cx.add_async_action(Editor::find_all_references);
302
303 workspace::register_project_item::<Editor>(cx);
304 workspace::register_followable_item::<Editor>(cx);
305}
306
307trait InvalidationRegion {
308 fn ranges(&self) -> &[Range<Anchor>];
309}
310
311#[derive(Clone, Debug)]
312pub enum SelectPhase {
313 Begin {
314 position: DisplayPoint,
315 add: bool,
316 click_count: usize,
317 },
318 BeginColumnar {
319 position: DisplayPoint,
320 overshoot: u32,
321 },
322 Extend {
323 position: DisplayPoint,
324 click_count: usize,
325 },
326 Update {
327 position: DisplayPoint,
328 overshoot: u32,
329 scroll_position: Vector2F,
330 },
331 End,
332}
333
334#[derive(Clone, Debug)]
335pub enum SelectMode {
336 Character,
337 Word(Range<Anchor>),
338 Line(Range<Anchor>),
339 All,
340}
341
342#[derive(PartialEq, Eq)]
343pub enum Autoscroll {
344 Fit,
345 Center,
346 Newest,
347}
348
349#[derive(Copy, Clone, PartialEq, Eq)]
350pub enum EditorMode {
351 SingleLine,
352 AutoHeight { max_lines: usize },
353 Full,
354}
355
356#[derive(Clone)]
357pub enum SoftWrap {
358 None,
359 EditorWidth,
360 Column(u32),
361}
362
363#[derive(Clone)]
364pub struct EditorStyle {
365 pub text: TextStyle,
366 pub placeholder_text: Option<TextStyle>,
367 pub theme: theme::Editor,
368}
369
370type CompletionId = usize;
371
372pub type GetFieldEditorTheme = fn(&theme::Theme) -> theme::FieldEditor;
373
374type OverrideTextStyle = dyn Fn(&EditorStyle) -> Option<HighlightStyle>;
375
376pub struct Editor {
377 handle: WeakViewHandle<Self>,
378 buffer: ModelHandle<MultiBuffer>,
379 display_map: ModelHandle<DisplayMap>,
380 pub selections: SelectionsCollection,
381 columnar_selection_tail: Option<Anchor>,
382 add_selections_state: Option<AddSelectionsState>,
383 select_next_state: Option<SelectNextState>,
384 selection_history: SelectionHistory,
385 autoclose_stack: InvalidationStack<BracketPairState>,
386 snippet_stack: InvalidationStack<SnippetState>,
387 select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
388 active_diagnostics: Option<ActiveDiagnosticGroup>,
389 scroll_position: Vector2F,
390 scroll_top_anchor: Anchor,
391 autoscroll_request: Option<(Autoscroll, bool)>,
392 soft_wrap_mode_override: Option<settings::SoftWrap>,
393 get_field_editor_theme: Option<GetFieldEditorTheme>,
394 override_text_style: Option<Box<OverrideTextStyle>>,
395 project: Option<ModelHandle<Project>>,
396 focused: bool,
397 show_local_cursors: bool,
398 show_local_selections: bool,
399 blink_epoch: usize,
400 blinking_paused: bool,
401 mode: EditorMode,
402 vertical_scroll_margin: f32,
403 placeholder_text: Option<Arc<str>>,
404 highlighted_rows: Option<Range<u32>>,
405 background_highlights: BTreeMap<TypeId, (fn(&Theme) -> Color, Vec<Range<Anchor>>)>,
406 nav_history: Option<ItemNavHistory>,
407 context_menu: Option<ContextMenu>,
408 completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
409 next_completion_id: CompletionId,
410 available_code_actions: Option<(ModelHandle<Buffer>, Arc<[CodeAction]>)>,
411 code_actions_task: Option<Task<()>>,
412 document_highlights_task: Option<Task<()>>,
413 pending_rename: Option<RenameState>,
414 searchable: bool,
415 cursor_shape: CursorShape,
416 keymap_context_layers: BTreeMap<TypeId, gpui::keymap::Context>,
417 input_enabled: bool,
418 leader_replica_id: Option<u16>,
419}
420
421pub struct EditorSnapshot {
422 pub mode: EditorMode,
423 pub display_snapshot: DisplaySnapshot,
424 pub placeholder_text: Option<Arc<str>>,
425 is_focused: bool,
426 scroll_position: Vector2F,
427 scroll_top_anchor: Anchor,
428}
429
430#[derive(Clone, Debug)]
431struct SelectionHistoryEntry {
432 selections: Arc<[Selection<Anchor>]>,
433 select_next_state: Option<SelectNextState>,
434 add_selections_state: Option<AddSelectionsState>,
435}
436
437enum SelectionHistoryMode {
438 Normal,
439 Undoing,
440 Redoing,
441}
442
443impl Default for SelectionHistoryMode {
444 fn default() -> Self {
445 Self::Normal
446 }
447}
448
449#[derive(Default)]
450struct SelectionHistory {
451 selections_by_transaction:
452 HashMap<TransactionId, (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)>,
453 mode: SelectionHistoryMode,
454 undo_stack: VecDeque<SelectionHistoryEntry>,
455 redo_stack: VecDeque<SelectionHistoryEntry>,
456}
457
458impl SelectionHistory {
459 fn insert_transaction(
460 &mut self,
461 transaction_id: TransactionId,
462 selections: Arc<[Selection<Anchor>]>,
463 ) {
464 self.selections_by_transaction
465 .insert(transaction_id, (selections, None));
466 }
467
468 fn transaction(
469 &self,
470 transaction_id: TransactionId,
471 ) -> Option<&(Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
472 self.selections_by_transaction.get(&transaction_id)
473 }
474
475 fn transaction_mut(
476 &mut self,
477 transaction_id: TransactionId,
478 ) -> Option<&mut (Arc<[Selection<Anchor>]>, Option<Arc<[Selection<Anchor>]>>)> {
479 self.selections_by_transaction.get_mut(&transaction_id)
480 }
481
482 fn push(&mut self, entry: SelectionHistoryEntry) {
483 if !entry.selections.is_empty() {
484 match self.mode {
485 SelectionHistoryMode::Normal => {
486 self.push_undo(entry);
487 self.redo_stack.clear();
488 }
489 SelectionHistoryMode::Undoing => self.push_redo(entry),
490 SelectionHistoryMode::Redoing => self.push_undo(entry),
491 }
492 }
493 }
494
495 fn push_undo(&mut self, entry: SelectionHistoryEntry) {
496 if self
497 .undo_stack
498 .back()
499 .map_or(true, |e| e.selections != entry.selections)
500 {
501 self.undo_stack.push_back(entry);
502 if self.undo_stack.len() > MAX_SELECTION_HISTORY_LEN {
503 self.undo_stack.pop_front();
504 }
505 }
506 }
507
508 fn push_redo(&mut self, entry: SelectionHistoryEntry) {
509 if self
510 .redo_stack
511 .back()
512 .map_or(true, |e| e.selections != entry.selections)
513 {
514 self.redo_stack.push_back(entry);
515 if self.redo_stack.len() > MAX_SELECTION_HISTORY_LEN {
516 self.redo_stack.pop_front();
517 }
518 }
519 }
520}
521
522#[derive(Clone, Debug)]
523struct AddSelectionsState {
524 above: bool,
525 stack: Vec<usize>,
526}
527
528#[derive(Clone, Debug)]
529struct SelectNextState {
530 query: AhoCorasick,
531 wordwise: bool,
532 done: bool,
533}
534
535struct BracketPairState {
536 ranges: Vec<Range<Anchor>>,
537 pair: BracketPair,
538}
539
540#[derive(Debug)]
541struct SnippetState {
542 ranges: Vec<Vec<Range<Anchor>>>,
543 active_index: usize,
544}
545
546pub struct RenameState {
547 pub range: Range<Anchor>,
548 pub old_name: Arc<str>,
549 pub editor: ViewHandle<Editor>,
550 block_id: BlockId,
551}
552
553struct InvalidationStack<T>(Vec<T>);
554
555enum ContextMenu {
556 Completions(CompletionsMenu),
557 CodeActions(CodeActionsMenu),
558}
559
560impl ContextMenu {
561 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) -> bool {
562 if self.visible() {
563 match self {
564 ContextMenu::Completions(menu) => menu.select_prev(cx),
565 ContextMenu::CodeActions(menu) => menu.select_prev(cx),
566 }
567 true
568 } else {
569 false
570 }
571 }
572
573 fn select_next(&mut self, cx: &mut ViewContext<Editor>) -> bool {
574 if self.visible() {
575 match self {
576 ContextMenu::Completions(menu) => menu.select_next(cx),
577 ContextMenu::CodeActions(menu) => menu.select_next(cx),
578 }
579 true
580 } else {
581 false
582 }
583 }
584
585 fn visible(&self) -> bool {
586 match self {
587 ContextMenu::Completions(menu) => menu.visible(),
588 ContextMenu::CodeActions(menu) => menu.visible(),
589 }
590 }
591
592 fn render(
593 &self,
594 cursor_position: DisplayPoint,
595 style: EditorStyle,
596 cx: &AppContext,
597 ) -> (DisplayPoint, ElementBox) {
598 match self {
599 ContextMenu::Completions(menu) => (cursor_position, menu.render(style, cx)),
600 ContextMenu::CodeActions(menu) => menu.render(cursor_position, style),
601 }
602 }
603}
604
605struct CompletionsMenu {
606 id: CompletionId,
607 initial_position: Anchor,
608 buffer: ModelHandle<Buffer>,
609 completions: Arc<[Completion]>,
610 match_candidates: Vec<StringMatchCandidate>,
611 matches: Arc<[StringMatch]>,
612 selected_item: usize,
613 list: UniformListState,
614}
615
616impl CompletionsMenu {
617 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
618 if self.selected_item > 0 {
619 self.selected_item -= 1;
620 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
621 }
622 cx.notify();
623 }
624
625 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
626 if self.selected_item + 1 < self.matches.len() {
627 self.selected_item += 1;
628 self.list.scroll_to(ScrollTarget::Show(self.selected_item));
629 }
630 cx.notify();
631 }
632
633 fn visible(&self) -> bool {
634 !self.matches.is_empty()
635 }
636
637 fn render(&self, style: EditorStyle, _: &AppContext) -> ElementBox {
638 enum CompletionTag {}
639
640 let completions = self.completions.clone();
641 let matches = self.matches.clone();
642 let selected_item = self.selected_item;
643 let container_style = style.autocomplete.container;
644 UniformList::new(self.list.clone(), matches.len(), move |range, items, cx| {
645 let start_ix = range.start;
646 for (ix, mat) in matches[range].iter().enumerate() {
647 let completion = &completions[mat.candidate_id];
648 let item_ix = start_ix + ix;
649 items.push(
650 MouseEventHandler::new::<CompletionTag, _, _>(
651 mat.candidate_id,
652 cx,
653 |state, _| {
654 let item_style = if item_ix == selected_item {
655 style.autocomplete.selected_item
656 } else if state.hovered {
657 style.autocomplete.hovered_item
658 } else {
659 style.autocomplete.item
660 };
661
662 Text::new(completion.label.text.clone(), style.text.clone())
663 .with_soft_wrap(false)
664 .with_highlights(combine_syntax_and_fuzzy_match_highlights(
665 &completion.label.text,
666 style.text.color.into(),
667 styled_runs_for_code_label(&completion.label, &style.syntax),
668 &mat.positions,
669 ))
670 .contained()
671 .with_style(item_style)
672 .boxed()
673 },
674 )
675 .with_cursor_style(CursorStyle::PointingHand)
676 .on_mouse_down(move |cx| {
677 cx.dispatch_action(ConfirmCompletion {
678 item_ix: Some(item_ix),
679 });
680 })
681 .boxed(),
682 );
683 }
684 })
685 .with_width_from_item(
686 self.matches
687 .iter()
688 .enumerate()
689 .max_by_key(|(_, mat)| {
690 self.completions[mat.candidate_id]
691 .label
692 .text
693 .chars()
694 .count()
695 })
696 .map(|(ix, _)| ix),
697 )
698 .contained()
699 .with_style(container_style)
700 .boxed()
701 }
702
703 pub async fn filter(&mut self, query: Option<&str>, executor: Arc<executor::Background>) {
704 let mut matches = if let Some(query) = query {
705 fuzzy::match_strings(
706 &self.match_candidates,
707 query,
708 false,
709 100,
710 &Default::default(),
711 executor,
712 )
713 .await
714 } else {
715 self.match_candidates
716 .iter()
717 .enumerate()
718 .map(|(candidate_id, candidate)| StringMatch {
719 candidate_id,
720 score: Default::default(),
721 positions: Default::default(),
722 string: candidate.string.clone(),
723 })
724 .collect()
725 };
726 matches.sort_unstable_by_key(|mat| {
727 (
728 Reverse(OrderedFloat(mat.score)),
729 self.completions[mat.candidate_id].sort_key(),
730 )
731 });
732
733 for mat in &mut matches {
734 let filter_start = self.completions[mat.candidate_id].label.filter_range.start;
735 for position in &mut mat.positions {
736 *position += filter_start;
737 }
738 }
739
740 self.matches = matches.into();
741 }
742}
743
744#[derive(Clone)]
745struct CodeActionsMenu {
746 actions: Arc<[CodeAction]>,
747 buffer: ModelHandle<Buffer>,
748 selected_item: usize,
749 list: UniformListState,
750 deployed_from_indicator: bool,
751}
752
753impl CodeActionsMenu {
754 fn select_prev(&mut self, cx: &mut ViewContext<Editor>) {
755 if self.selected_item > 0 {
756 self.selected_item -= 1;
757 cx.notify()
758 }
759 }
760
761 fn select_next(&mut self, cx: &mut ViewContext<Editor>) {
762 if self.selected_item + 1 < self.actions.len() {
763 self.selected_item += 1;
764 cx.notify()
765 }
766 }
767
768 fn visible(&self) -> bool {
769 !self.actions.is_empty()
770 }
771
772 fn render(
773 &self,
774 mut cursor_position: DisplayPoint,
775 style: EditorStyle,
776 ) -> (DisplayPoint, ElementBox) {
777 enum ActionTag {}
778
779 let container_style = style.autocomplete.container;
780 let actions = self.actions.clone();
781 let selected_item = self.selected_item;
782 let element =
783 UniformList::new(self.list.clone(), actions.len(), move |range, items, cx| {
784 let start_ix = range.start;
785 for (ix, action) in actions[range].iter().enumerate() {
786 let item_ix = start_ix + ix;
787 items.push(
788 MouseEventHandler::new::<ActionTag, _, _>(item_ix, cx, |state, _| {
789 let item_style = if item_ix == selected_item {
790 style.autocomplete.selected_item
791 } else if state.hovered {
792 style.autocomplete.hovered_item
793 } else {
794 style.autocomplete.item
795 };
796
797 Text::new(action.lsp_action.title.clone(), style.text.clone())
798 .with_soft_wrap(false)
799 .contained()
800 .with_style(item_style)
801 .boxed()
802 })
803 .with_cursor_style(CursorStyle::PointingHand)
804 .on_mouse_down(move |cx| {
805 cx.dispatch_action(ConfirmCodeAction {
806 item_ix: Some(item_ix),
807 });
808 })
809 .boxed(),
810 );
811 }
812 })
813 .with_width_from_item(
814 self.actions
815 .iter()
816 .enumerate()
817 .max_by_key(|(_, action)| action.lsp_action.title.chars().count())
818 .map(|(ix, _)| ix),
819 )
820 .contained()
821 .with_style(container_style)
822 .boxed();
823
824 if self.deployed_from_indicator {
825 *cursor_position.column_mut() = 0;
826 }
827
828 (cursor_position, element)
829 }
830}
831
832#[derive(Debug)]
833struct ActiveDiagnosticGroup {
834 primary_range: Range<Anchor>,
835 primary_message: String,
836 blocks: HashMap<BlockId, Diagnostic>,
837 is_valid: bool,
838}
839
840#[derive(Serialize, Deserialize)]
841struct ClipboardSelection {
842 len: usize,
843 is_entire_line: bool,
844}
845
846#[derive(Debug)]
847pub struct NavigationData {
848 // Matching offsets for anchor and scroll_top_anchor allows us to recreate the anchor if the buffer
849 // has since been closed
850 cursor_anchor: Anchor,
851 cursor_position: Point,
852 scroll_position: Vector2F,
853 scroll_top_anchor: Anchor,
854 scroll_top_row: u32,
855}
856
857pub struct EditorCreated(pub ViewHandle<Editor>);
858
859impl Editor {
860 pub fn single_line(
861 field_editor_style: Option<GetFieldEditorTheme>,
862 cx: &mut ViewContext<Self>,
863 ) -> Self {
864 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
865 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
866 Self::new(EditorMode::SingleLine, buffer, None, field_editor_style, cx)
867 }
868
869 pub fn auto_height(
870 max_lines: usize,
871 field_editor_style: Option<GetFieldEditorTheme>,
872 cx: &mut ViewContext<Self>,
873 ) -> Self {
874 let buffer = cx.add_model(|cx| Buffer::new(0, String::new(), cx));
875 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
876 Self::new(
877 EditorMode::AutoHeight { max_lines },
878 buffer,
879 None,
880 field_editor_style,
881 cx,
882 )
883 }
884
885 pub fn for_buffer(
886 buffer: ModelHandle<Buffer>,
887 project: Option<ModelHandle<Project>>,
888 cx: &mut ViewContext<Self>,
889 ) -> Self {
890 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
891 Self::new(EditorMode::Full, buffer, project, None, cx)
892 }
893
894 pub fn for_multibuffer(
895 buffer: ModelHandle<MultiBuffer>,
896 project: Option<ModelHandle<Project>>,
897 cx: &mut ViewContext<Self>,
898 ) -> Self {
899 Self::new(EditorMode::Full, buffer, project, None, cx)
900 }
901
902 pub fn clone(&self, cx: &mut ViewContext<Self>) -> Self {
903 let mut clone = Self::new(
904 self.mode,
905 self.buffer.clone(),
906 self.project.clone(),
907 self.get_field_editor_theme,
908 cx,
909 );
910 clone.scroll_position = self.scroll_position;
911 clone.scroll_top_anchor = self.scroll_top_anchor.clone();
912 clone.searchable = self.searchable;
913 clone
914 }
915
916 fn new(
917 mode: EditorMode,
918 buffer: ModelHandle<MultiBuffer>,
919 project: Option<ModelHandle<Project>>,
920 get_field_editor_theme: Option<GetFieldEditorTheme>,
921 cx: &mut ViewContext<Self>,
922 ) -> Self {
923 let display_map = cx.add_model(|cx| {
924 let settings = cx.global::<Settings>();
925 let style = build_style(&*settings, get_field_editor_theme, None, cx);
926 DisplayMap::new(
927 buffer.clone(),
928 style.text.font_id,
929 style.text.font_size,
930 None,
931 2,
932 1,
933 cx,
934 )
935 });
936 cx.observe(&buffer, Self::on_buffer_changed).detach();
937 cx.subscribe(&buffer, Self::on_buffer_event).detach();
938 cx.observe(&display_map, Self::on_display_map_changed)
939 .detach();
940
941 let mut this = Self {
942 handle: cx.weak_handle(),
943 buffer,
944 display_map,
945 selections: SelectionsCollection::new(),
946 columnar_selection_tail: None,
947 add_selections_state: None,
948 select_next_state: None,
949 selection_history: Default::default(),
950 autoclose_stack: Default::default(),
951 snippet_stack: Default::default(),
952 select_larger_syntax_node_stack: Vec::new(),
953 active_diagnostics: None,
954 soft_wrap_mode_override: None,
955 get_field_editor_theme,
956 project,
957 scroll_position: Vector2F::zero(),
958 scroll_top_anchor: Anchor::min(),
959 autoscroll_request: None,
960 focused: false,
961 show_local_cursors: false,
962 show_local_selections: true,
963 blink_epoch: 0,
964 blinking_paused: false,
965 mode,
966 vertical_scroll_margin: 3.0,
967 placeholder_text: None,
968 highlighted_rows: None,
969 background_highlights: Default::default(),
970 nav_history: None,
971 context_menu: None,
972 completion_tasks: Default::default(),
973 next_completion_id: 0,
974 available_code_actions: Default::default(),
975 code_actions_task: Default::default(),
976 document_highlights_task: Default::default(),
977 pending_rename: Default::default(),
978 searchable: true,
979 override_text_style: None,
980 cursor_shape: Default::default(),
981 keymap_context_layers: Default::default(),
982 input_enabled: true,
983 leader_replica_id: None,
984 };
985 this.end_selection(cx);
986
987 let editor_created_event = EditorCreated(cx.handle());
988 cx.emit_global(editor_created_event);
989
990 this
991 }
992
993 pub fn open_new(
994 workspace: &mut Workspace,
995 _: &workspace::OpenNew,
996 cx: &mut ViewContext<Workspace>,
997 ) {
998 let project = workspace.project().clone();
999 if project.read(cx).is_remote() {
1000 cx.propagate_action();
1001 } else if let Some(buffer) = project
1002 .update(cx, |project, cx| project.create_buffer("", None, cx))
1003 .log_err()
1004 {
1005 workspace.add_item(
1006 Box::new(cx.add_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx))),
1007 cx,
1008 );
1009 }
1010 }
1011
1012 pub fn replica_id(&self, cx: &AppContext) -> ReplicaId {
1013 self.buffer.read(cx).replica_id()
1014 }
1015
1016 pub fn buffer(&self) -> &ModelHandle<MultiBuffer> {
1017 &self.buffer
1018 }
1019
1020 pub fn title(&self, cx: &AppContext) -> String {
1021 self.buffer().read(cx).title(cx)
1022 }
1023
1024 pub fn snapshot(&mut self, cx: &mut MutableAppContext) -> EditorSnapshot {
1025 EditorSnapshot {
1026 mode: self.mode,
1027 display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
1028 scroll_position: self.scroll_position,
1029 scroll_top_anchor: self.scroll_top_anchor.clone(),
1030 placeholder_text: self.placeholder_text.clone(),
1031 is_focused: self
1032 .handle
1033 .upgrade(cx)
1034 .map_or(false, |handle| handle.is_focused(cx)),
1035 }
1036 }
1037
1038 pub fn language_at<'a, T: ToOffset>(
1039 &self,
1040 point: T,
1041 cx: &'a AppContext,
1042 ) -> Option<&'a Arc<Language>> {
1043 self.buffer.read(cx).language_at(point, cx)
1044 }
1045
1046 fn style(&self, cx: &AppContext) -> EditorStyle {
1047 build_style(
1048 cx.global::<Settings>(),
1049 self.get_field_editor_theme,
1050 self.override_text_style.as_deref(),
1051 cx,
1052 )
1053 }
1054
1055 pub fn mode(&self) -> EditorMode {
1056 self.mode
1057 }
1058
1059 pub fn set_placeholder_text(
1060 &mut self,
1061 placeholder_text: impl Into<Arc<str>>,
1062 cx: &mut ViewContext<Self>,
1063 ) {
1064 self.placeholder_text = Some(placeholder_text.into());
1065 cx.notify();
1066 }
1067
1068 pub fn set_vertical_scroll_margin(&mut self, margin_rows: usize, cx: &mut ViewContext<Self>) {
1069 self.vertical_scroll_margin = margin_rows as f32;
1070 cx.notify();
1071 }
1072
1073 pub fn set_scroll_position(&mut self, scroll_position: Vector2F, cx: &mut ViewContext<Self>) {
1074 self.set_scroll_position_internal(scroll_position, true, cx);
1075 }
1076
1077 fn set_scroll_position_internal(
1078 &mut self,
1079 scroll_position: Vector2F,
1080 local: bool,
1081 cx: &mut ViewContext<Self>,
1082 ) {
1083 let map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1084
1085 if scroll_position.y() == 0. {
1086 self.scroll_top_anchor = Anchor::min();
1087 self.scroll_position = scroll_position;
1088 } else {
1089 let scroll_top_buffer_offset =
1090 DisplayPoint::new(scroll_position.y() as u32, 0).to_offset(&map, Bias::Right);
1091 let anchor = map
1092 .buffer_snapshot
1093 .anchor_at(scroll_top_buffer_offset, Bias::Right);
1094 self.scroll_position = vec2f(
1095 scroll_position.x(),
1096 scroll_position.y() - anchor.to_display_point(&map).row() as f32,
1097 );
1098 self.scroll_top_anchor = anchor;
1099 }
1100
1101 self.autoscroll_request.take();
1102 cx.emit(Event::ScrollPositionChanged { local });
1103 cx.notify();
1104 }
1105
1106 fn set_scroll_top_anchor(
1107 &mut self,
1108 anchor: Anchor,
1109 position: Vector2F,
1110 cx: &mut ViewContext<Self>,
1111 ) {
1112 self.scroll_top_anchor = anchor;
1113 self.scroll_position = position;
1114 cx.emit(Event::ScrollPositionChanged { local: false });
1115 cx.notify();
1116 }
1117
1118 pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
1119 self.cursor_shape = cursor_shape;
1120 cx.notify();
1121 }
1122
1123 pub fn set_clip_at_line_ends(&mut self, clip: bool, cx: &mut ViewContext<Self>) {
1124 if self.display_map.read(cx).clip_at_line_ends != clip {
1125 self.display_map
1126 .update(cx, |map, _| map.clip_at_line_ends = clip);
1127 }
1128 }
1129
1130 pub fn set_keymap_context_layer<Tag: 'static>(&mut self, context: gpui::keymap::Context) {
1131 self.keymap_context_layers
1132 .insert(TypeId::of::<Tag>(), context);
1133 }
1134
1135 pub fn remove_keymap_context_layer<Tag: 'static>(&mut self) {
1136 self.keymap_context_layers.remove(&TypeId::of::<Tag>());
1137 }
1138
1139 pub fn set_input_enabled(&mut self, input_enabled: bool) {
1140 self.input_enabled = input_enabled;
1141 }
1142
1143 pub fn scroll_position(&self, cx: &mut ViewContext<Self>) -> Vector2F {
1144 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1145 compute_scroll_position(&display_map, self.scroll_position, &self.scroll_top_anchor)
1146 }
1147
1148 pub fn clamp_scroll_left(&mut self, max: f32) -> bool {
1149 if max < self.scroll_position.x() {
1150 self.scroll_position.set_x(max);
1151 true
1152 } else {
1153 false
1154 }
1155 }
1156
1157 pub fn autoscroll_vertically(
1158 &mut self,
1159 viewport_height: f32,
1160 line_height: f32,
1161 cx: &mut ViewContext<Self>,
1162 ) -> bool {
1163 let visible_lines = viewport_height / line_height;
1164 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1165 let mut scroll_position =
1166 compute_scroll_position(&display_map, self.scroll_position, &self.scroll_top_anchor);
1167 let max_scroll_top = if matches!(self.mode, EditorMode::AutoHeight { .. }) {
1168 (display_map.max_point().row() as f32 - visible_lines + 1.).max(0.)
1169 } else {
1170 display_map.max_point().row().saturating_sub(1) as f32
1171 };
1172 if scroll_position.y() > max_scroll_top {
1173 scroll_position.set_y(max_scroll_top);
1174 self.set_scroll_position(scroll_position, cx);
1175 }
1176
1177 let (autoscroll, local) = if let Some(autoscroll) = self.autoscroll_request.take() {
1178 autoscroll
1179 } else {
1180 return false;
1181 };
1182
1183 let first_cursor_top;
1184 let last_cursor_bottom;
1185 if let Some(highlighted_rows) = &self.highlighted_rows {
1186 first_cursor_top = highlighted_rows.start as f32;
1187 last_cursor_bottom = first_cursor_top + 1.;
1188 } else if autoscroll == Autoscroll::Newest {
1189 let newest_selection = self
1190 .selections
1191 .newest::<Point>(&display_map.buffer_snapshot);
1192 first_cursor_top = newest_selection.head().to_display_point(&display_map).row() as f32;
1193 last_cursor_bottom = first_cursor_top + 1.;
1194 } else {
1195 let selections = self
1196 .selections
1197 .interleaved::<Point>(&display_map.buffer_snapshot);
1198 first_cursor_top = selections
1199 .first()
1200 .unwrap()
1201 .head()
1202 .to_display_point(&display_map)
1203 .row() as f32;
1204 last_cursor_bottom = selections
1205 .last()
1206 .unwrap()
1207 .head()
1208 .to_display_point(&display_map)
1209 .row() as f32
1210 + 1.0;
1211 }
1212
1213 let margin = if matches!(self.mode, EditorMode::AutoHeight { .. }) {
1214 0.
1215 } else {
1216 ((visible_lines - (last_cursor_bottom - first_cursor_top)) / 2.0).floor()
1217 };
1218 if margin < 0.0 {
1219 return false;
1220 }
1221
1222 match autoscroll {
1223 Autoscroll::Fit | Autoscroll::Newest => {
1224 let margin = margin.min(self.vertical_scroll_margin);
1225 let target_top = (first_cursor_top - margin).max(0.0);
1226 let target_bottom = last_cursor_bottom + margin;
1227 let start_row = scroll_position.y();
1228 let end_row = start_row + visible_lines;
1229
1230 if target_top < start_row {
1231 scroll_position.set_y(target_top);
1232 self.set_scroll_position_internal(scroll_position, local, cx);
1233 } else if target_bottom >= end_row {
1234 scroll_position.set_y(target_bottom - visible_lines);
1235 self.set_scroll_position_internal(scroll_position, local, cx);
1236 }
1237 }
1238 Autoscroll::Center => {
1239 scroll_position.set_y((first_cursor_top - margin).max(0.0));
1240 self.set_scroll_position_internal(scroll_position, local, cx);
1241 }
1242 }
1243
1244 true
1245 }
1246
1247 pub fn autoscroll_horizontally(
1248 &mut self,
1249 start_row: u32,
1250 viewport_width: f32,
1251 scroll_width: f32,
1252 max_glyph_width: f32,
1253 layouts: &[text_layout::Line],
1254 cx: &mut ViewContext<Self>,
1255 ) -> bool {
1256 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1257 let selections = self
1258 .selections
1259 .interleaved::<Point>(&display_map.buffer_snapshot);
1260
1261 let mut target_left;
1262 let mut target_right;
1263
1264 if self.highlighted_rows.is_some() {
1265 target_left = 0.0_f32;
1266 target_right = 0.0_f32;
1267 } else {
1268 target_left = std::f32::INFINITY;
1269 target_right = 0.0_f32;
1270 for selection in selections {
1271 let head = selection.head().to_display_point(&display_map);
1272 if head.row() >= start_row && head.row() < start_row + layouts.len() as u32 {
1273 let start_column = head.column().saturating_sub(3);
1274 let end_column = cmp::min(display_map.line_len(head.row()), head.column() + 3);
1275 target_left = target_left.min(
1276 layouts[(head.row() - start_row) as usize]
1277 .x_for_index(start_column as usize),
1278 );
1279 target_right = target_right.max(
1280 layouts[(head.row() - start_row) as usize].x_for_index(end_column as usize)
1281 + max_glyph_width,
1282 );
1283 }
1284 }
1285 }
1286
1287 target_right = target_right.min(scroll_width);
1288
1289 if target_right - target_left > viewport_width {
1290 return false;
1291 }
1292
1293 let scroll_left = self.scroll_position.x() * max_glyph_width;
1294 let scroll_right = scroll_left + viewport_width;
1295
1296 if target_left < scroll_left {
1297 self.scroll_position.set_x(target_left / max_glyph_width);
1298 true
1299 } else if target_right > scroll_right {
1300 self.scroll_position
1301 .set_x((target_right - viewport_width) / max_glyph_width);
1302 true
1303 } else {
1304 false
1305 }
1306 }
1307
1308 fn selections_did_change(
1309 &mut self,
1310 local: bool,
1311 old_cursor_position: &Anchor,
1312 cx: &mut ViewContext<Self>,
1313 ) {
1314 if self.focused && self.leader_replica_id.is_none() {
1315 self.buffer.update(cx, |buffer, cx| {
1316 buffer.set_active_selections(&self.selections.disjoint_anchors(), cx)
1317 });
1318 }
1319
1320 let display_map = self
1321 .display_map
1322 .update(cx, |display_map, cx| display_map.snapshot(cx));
1323 let buffer = &display_map.buffer_snapshot;
1324 self.add_selections_state = None;
1325 self.select_next_state = None;
1326 self.select_larger_syntax_node_stack.clear();
1327 self.autoclose_stack
1328 .invalidate(&self.selections.disjoint_anchors(), buffer);
1329 self.snippet_stack
1330 .invalidate(&self.selections.disjoint_anchors(), buffer);
1331 self.take_rename(false, cx);
1332
1333 let new_cursor_position = self.selections.newest_anchor().head();
1334
1335 self.push_to_nav_history(
1336 old_cursor_position.clone(),
1337 Some(new_cursor_position.to_point(buffer)),
1338 cx,
1339 );
1340
1341 if local {
1342 let new_cursor_position = self.selections.newest_anchor().head();
1343 let completion_menu = match self.context_menu.as_mut() {
1344 Some(ContextMenu::Completions(menu)) => Some(menu),
1345 _ => {
1346 self.context_menu.take();
1347 None
1348 }
1349 };
1350
1351 if let Some(completion_menu) = completion_menu {
1352 let cursor_position = new_cursor_position.to_offset(buffer);
1353 let (word_range, kind) =
1354 buffer.surrounding_word(completion_menu.initial_position.clone());
1355 if kind == Some(CharKind::Word)
1356 && word_range.to_inclusive().contains(&cursor_position)
1357 {
1358 let query = Self::completion_query(buffer, cursor_position);
1359 cx.background()
1360 .block(completion_menu.filter(query.as_deref(), cx.background().clone()));
1361 self.show_completions(&ShowCompletions, cx);
1362 } else {
1363 self.hide_context_menu(cx);
1364 }
1365 }
1366
1367 if old_cursor_position.to_display_point(&display_map).row()
1368 != new_cursor_position.to_display_point(&display_map).row()
1369 {
1370 self.available_code_actions.take();
1371 }
1372 self.refresh_code_actions(cx);
1373 self.refresh_document_highlights(cx);
1374 }
1375
1376 self.pause_cursor_blinking(cx);
1377 cx.emit(Event::SelectionsChanged { local });
1378 cx.notify();
1379 }
1380
1381 pub fn change_selections<R>(
1382 &mut self,
1383 autoscroll: Option<Autoscroll>,
1384 cx: &mut ViewContext<Self>,
1385 change: impl FnOnce(&mut MutableSelectionsCollection<'_>) -> R,
1386 ) -> R {
1387 let old_cursor_position = self.selections.newest_anchor().head();
1388 self.push_to_selection_history();
1389
1390 let result =
1391 self.selections
1392 .change_with(self.display_map.clone(), self.buffer.clone(), cx, change);
1393
1394 if let Some(autoscroll) = autoscroll {
1395 self.request_autoscroll(autoscroll, cx);
1396 }
1397 self.selections_did_change(true, &old_cursor_position, cx);
1398
1399 result
1400 }
1401
1402 pub fn display_selections(
1403 &mut self,
1404 cx: &mut ViewContext<Self>,
1405 ) -> (DisplaySnapshot, Vec<Selection<DisplayPoint>>) {
1406 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1407 let selections = self
1408 .selections
1409 .interleaved::<Point>(&display_map.buffer_snapshot)
1410 .into_iter()
1411 .map(|selection| selection.map(|point| point.to_display_point(&display_map)))
1412 .collect();
1413 (display_map, selections)
1414 }
1415
1416 pub fn edit<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1417 where
1418 I: IntoIterator<Item = (Range<S>, T)>,
1419 S: ToOffset,
1420 T: Into<Arc<str>>,
1421 {
1422 self.buffer.update(cx, |buffer, cx| buffer.edit(edits, cx));
1423 }
1424
1425 pub fn edit_with_autoindent<I, S, T>(&mut self, edits: I, cx: &mut ViewContext<Self>)
1426 where
1427 I: IntoIterator<Item = (Range<S>, T)>,
1428 S: ToOffset,
1429 T: Into<Arc<str>>,
1430 {
1431 self.buffer
1432 .update(cx, |buffer, cx| buffer.edit_with_autoindent(edits, cx));
1433 }
1434
1435 fn select(&mut self, Select(phase): &Select, cx: &mut ViewContext<Self>) {
1436 self.hide_context_menu(cx);
1437
1438 match phase {
1439 SelectPhase::Begin {
1440 position,
1441 add,
1442 click_count,
1443 } => self.begin_selection(*position, *add, *click_count, cx),
1444 SelectPhase::BeginColumnar {
1445 position,
1446 overshoot,
1447 } => self.begin_columnar_selection(*position, *overshoot, cx),
1448 SelectPhase::Extend {
1449 position,
1450 click_count,
1451 } => self.extend_selection(*position, *click_count, cx),
1452 SelectPhase::Update {
1453 position,
1454 overshoot,
1455 scroll_position,
1456 } => self.update_selection(*position, *overshoot, *scroll_position, cx),
1457 SelectPhase::End => self.end_selection(cx),
1458 }
1459 }
1460
1461 fn extend_selection(
1462 &mut self,
1463 position: DisplayPoint,
1464 click_count: usize,
1465 cx: &mut ViewContext<Self>,
1466 ) {
1467 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1468 let tail = self
1469 .selections
1470 .newest::<usize>(&display_map.buffer_snapshot)
1471 .tail();
1472 self.begin_selection(position, false, click_count, cx);
1473
1474 let position = position.to_offset(&display_map, Bias::Left);
1475 let tail_anchor = display_map.buffer_snapshot.anchor_before(tail);
1476
1477 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
1478 let mut pending = s
1479 .pending_mut()
1480 .as_mut()
1481 .expect("extend_selection not called with pending selection");
1482
1483 if position >= tail {
1484 pending.selection.start = tail_anchor.clone();
1485 } else {
1486 pending.selection.end = tail_anchor.clone();
1487 pending.selection.reversed = true;
1488 }
1489
1490 match &mut pending.mode {
1491 SelectMode::Word(range) | SelectMode::Line(range) => {
1492 *range = tail_anchor.clone()..tail_anchor
1493 }
1494 _ => {}
1495 }
1496 });
1497 }
1498
1499 fn begin_selection(
1500 &mut self,
1501 position: DisplayPoint,
1502 add: bool,
1503 click_count: usize,
1504 cx: &mut ViewContext<Self>,
1505 ) {
1506 if !self.focused {
1507 cx.focus_self();
1508 cx.emit(Event::Activate);
1509 }
1510
1511 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1512 let buffer = &display_map.buffer_snapshot;
1513 let newest_selection = self.selections.newest_anchor().clone();
1514 let position = display_map.clip_point(position, Bias::Left);
1515
1516 let start;
1517 let end;
1518 let mode;
1519 match click_count {
1520 1 => {
1521 start = buffer.anchor_before(position.to_point(&display_map));
1522 end = start.clone();
1523 mode = SelectMode::Character;
1524 }
1525 2 => {
1526 let range = movement::surrounding_word(&display_map, position);
1527 start = buffer.anchor_before(range.start.to_point(&display_map));
1528 end = buffer.anchor_before(range.end.to_point(&display_map));
1529 mode = SelectMode::Word(start.clone()..end.clone());
1530 }
1531 3 => {
1532 let position = display_map
1533 .clip_point(position, Bias::Left)
1534 .to_point(&display_map);
1535 let line_start = display_map.prev_line_boundary(position).0;
1536 let next_line_start = buffer.clip_point(
1537 display_map.next_line_boundary(position).0 + Point::new(1, 0),
1538 Bias::Left,
1539 );
1540 start = buffer.anchor_before(line_start);
1541 end = buffer.anchor_before(next_line_start);
1542 mode = SelectMode::Line(start.clone()..end.clone());
1543 }
1544 _ => {
1545 start = buffer.anchor_before(0);
1546 end = buffer.anchor_before(buffer.len());
1547 mode = SelectMode::All;
1548 }
1549 }
1550
1551 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
1552 if add {
1553 if click_count > 1 {
1554 s.delete(newest_selection.id);
1555 }
1556 } else {
1557 s.clear_disjoint();
1558 }
1559
1560 s.set_pending_range(start..end, mode);
1561 });
1562 }
1563
1564 fn begin_columnar_selection(
1565 &mut self,
1566 position: DisplayPoint,
1567 overshoot: u32,
1568 cx: &mut ViewContext<Self>,
1569 ) {
1570 if !self.focused {
1571 cx.focus_self();
1572 cx.emit(Event::Activate);
1573 }
1574
1575 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1576 let tail = self
1577 .selections
1578 .newest::<Point>(&display_map.buffer_snapshot)
1579 .tail();
1580 self.columnar_selection_tail = Some(display_map.buffer_snapshot.anchor_before(tail));
1581
1582 self.select_columns(
1583 tail.to_display_point(&display_map),
1584 position,
1585 overshoot,
1586 &display_map,
1587 cx,
1588 );
1589 }
1590
1591 fn update_selection(
1592 &mut self,
1593 position: DisplayPoint,
1594 overshoot: u32,
1595 scroll_position: Vector2F,
1596 cx: &mut ViewContext<Self>,
1597 ) {
1598 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
1599
1600 if let Some(tail) = self.columnar_selection_tail.as_ref() {
1601 let tail = tail.to_display_point(&display_map);
1602 self.select_columns(tail, position, overshoot, &display_map, cx);
1603 } else if let Some(mut pending) = self.selections.pending_anchor().clone() {
1604 let buffer = self.buffer.read(cx).snapshot(cx);
1605 let head;
1606 let tail;
1607 match &self.selections.pending_mode().unwrap() {
1608 SelectMode::Character => {
1609 head = position.to_point(&display_map);
1610 tail = pending.tail().to_point(&buffer);
1611 }
1612 SelectMode::Word(original_range) => {
1613 let original_display_range = original_range.start.to_display_point(&display_map)
1614 ..original_range.end.to_display_point(&display_map);
1615 let original_buffer_range = original_display_range.start.to_point(&display_map)
1616 ..original_display_range.end.to_point(&display_map);
1617 if movement::is_inside_word(&display_map, position)
1618 || original_display_range.contains(&position)
1619 {
1620 let word_range = movement::surrounding_word(&display_map, position);
1621 if word_range.start < original_display_range.start {
1622 head = word_range.start.to_point(&display_map);
1623 } else {
1624 head = word_range.end.to_point(&display_map);
1625 }
1626 } else {
1627 head = position.to_point(&display_map);
1628 }
1629
1630 if head <= original_buffer_range.start {
1631 tail = original_buffer_range.end;
1632 } else {
1633 tail = original_buffer_range.start;
1634 }
1635 }
1636 SelectMode::Line(original_range) => {
1637 let original_range = original_range.to_point(&display_map.buffer_snapshot);
1638
1639 let position = display_map
1640 .clip_point(position, Bias::Left)
1641 .to_point(&display_map);
1642 let line_start = display_map.prev_line_boundary(position).0;
1643 let next_line_start = buffer.clip_point(
1644 display_map.next_line_boundary(position).0 + Point::new(1, 0),
1645 Bias::Left,
1646 );
1647
1648 if line_start < original_range.start {
1649 head = line_start
1650 } else {
1651 head = next_line_start
1652 }
1653
1654 if head <= original_range.start {
1655 tail = original_range.end;
1656 } else {
1657 tail = original_range.start;
1658 }
1659 }
1660 SelectMode::All => {
1661 return;
1662 }
1663 };
1664
1665 if head < tail {
1666 pending.start = buffer.anchor_before(head);
1667 pending.end = buffer.anchor_before(tail);
1668 pending.reversed = true;
1669 } else {
1670 pending.start = buffer.anchor_before(tail);
1671 pending.end = buffer.anchor_before(head);
1672 pending.reversed = false;
1673 }
1674
1675 self.change_selections(None, cx, |s| {
1676 s.pending_mut().as_mut().unwrap().selection = pending;
1677 });
1678 } else {
1679 log::error!("update_selection dispatched with no pending selection");
1680 return;
1681 }
1682
1683 self.set_scroll_position(scroll_position, cx);
1684 cx.notify();
1685 }
1686
1687 fn end_selection(&mut self, cx: &mut ViewContext<Self>) {
1688 self.columnar_selection_tail.take();
1689 if self.selections.pending_anchor().is_some() {
1690 let selections = self
1691 .selections
1692 .interleaved::<usize>(&self.buffer.read(cx).snapshot(cx));
1693 self.change_selections(None, cx, |s| {
1694 s.select(selections);
1695 s.clear_pending();
1696 });
1697 }
1698 }
1699
1700 fn select_columns(
1701 &mut self,
1702 tail: DisplayPoint,
1703 head: DisplayPoint,
1704 overshoot: u32,
1705 display_map: &DisplaySnapshot,
1706 cx: &mut ViewContext<Self>,
1707 ) {
1708 let start_row = cmp::min(tail.row(), head.row());
1709 let end_row = cmp::max(tail.row(), head.row());
1710 let start_column = cmp::min(tail.column(), head.column() + overshoot);
1711 let end_column = cmp::max(tail.column(), head.column() + overshoot);
1712 let reversed = start_column < tail.column();
1713
1714 let selection_ranges = (start_row..=end_row)
1715 .filter_map(|row| {
1716 if start_column <= display_map.line_len(row) && !display_map.is_block_line(row) {
1717 let start = display_map
1718 .clip_point(DisplayPoint::new(row, start_column), Bias::Left)
1719 .to_point(&display_map);
1720 let end = display_map
1721 .clip_point(DisplayPoint::new(row, end_column), Bias::Right)
1722 .to_point(&display_map);
1723 if reversed {
1724 Some(end..start)
1725 } else {
1726 Some(start..end)
1727 }
1728 } else {
1729 None
1730 }
1731 })
1732 .collect::<Vec<_>>();
1733
1734 self.change_selections(None, cx, |s| {
1735 s.select_ranges(selection_ranges);
1736 });
1737 cx.notify();
1738 }
1739
1740 pub fn is_selecting(&self) -> bool {
1741 self.selections.pending_anchor().is_some() || self.columnar_selection_tail.is_some()
1742 }
1743
1744 pub fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
1745 if self.take_rename(false, cx).is_some() {
1746 return;
1747 }
1748
1749 if self.hide_context_menu(cx).is_some() {
1750 return;
1751 }
1752
1753 if self.snippet_stack.pop().is_some() {
1754 return;
1755 }
1756
1757 if self.mode == EditorMode::Full {
1758 if self.active_diagnostics.is_some() {
1759 self.dismiss_diagnostics(cx);
1760 return;
1761 }
1762
1763 if self.change_selections(None, cx, |s| s.try_cancel()) {
1764 self.request_autoscroll(Autoscroll::Fit, cx);
1765 return;
1766 }
1767 }
1768
1769 cx.propagate_action();
1770 }
1771
1772 #[cfg(any(test, feature = "test-support"))]
1773 pub fn selected_ranges<D: TextDimension + Ord + Sub<D, Output = D> + std::fmt::Debug>(
1774 &self,
1775 cx: &AppContext,
1776 ) -> Vec<Range<D>> {
1777 self.selections
1778 .interleaved::<D>(&self.buffer.read(cx).read(cx))
1779 .iter()
1780 .map(|s| {
1781 if s.reversed {
1782 s.end.clone()..s.start.clone()
1783 } else {
1784 s.start.clone()..s.end.clone()
1785 }
1786 })
1787 .collect()
1788 }
1789
1790 #[cfg(any(test, feature = "test-support"))]
1791 pub fn selected_display_ranges(&self, cx: &mut MutableAppContext) -> Vec<Range<DisplayPoint>> {
1792 let display_map = self
1793 .display_map
1794 .update(cx, |display_map, cx| display_map.snapshot(cx));
1795 self.selections
1796 .disjoint_anchors()
1797 .iter()
1798 .chain(self.selections.pending_anchor().as_ref())
1799 .map(|s| {
1800 if s.reversed {
1801 s.end.to_display_point(&display_map)..s.start.to_display_point(&display_map)
1802 } else {
1803 s.start.to_display_point(&display_map)..s.end.to_display_point(&display_map)
1804 }
1805 })
1806 .collect()
1807 }
1808
1809 pub fn handle_input(&mut self, action: &Input, cx: &mut ViewContext<Self>) {
1810 if !self.input_enabled {
1811 cx.propagate_action();
1812 return;
1813 }
1814
1815 let text = action.0.as_ref();
1816 if !self.skip_autoclose_end(text, cx) {
1817 self.transact(cx, |this, cx| {
1818 if !this.surround_with_bracket_pair(text, cx) {
1819 this.insert(text, cx);
1820 this.autoclose_bracket_pairs(cx);
1821 }
1822 });
1823 self.trigger_completion_on_input(text, cx);
1824 }
1825 }
1826
1827 pub fn newline(&mut self, _: &Newline, cx: &mut ViewContext<Self>) {
1828 self.transact(cx, |this, cx| {
1829 let (edits, selection_fixup_info): (Vec<_>, Vec<_>) = {
1830 let buffer = this.buffer.read(cx).snapshot(cx);
1831 let selections = this.selections.interleaved::<usize>(&buffer);
1832 selections
1833 .iter()
1834 .map(|selection| {
1835 let start_point = selection.start.to_point(&buffer);
1836 let indent = buffer
1837 .indent_column_for_line(start_point.row)
1838 .min(start_point.column);
1839 let start = selection.start;
1840 let end = selection.end;
1841
1842 let mut insert_extra_newline = false;
1843 if let Some(language) = buffer.language() {
1844 let leading_whitespace_len = buffer
1845 .reversed_chars_at(start)
1846 .take_while(|c| c.is_whitespace() && *c != '\n')
1847 .map(|c| c.len_utf8())
1848 .sum::<usize>();
1849
1850 let trailing_whitespace_len = buffer
1851 .chars_at(end)
1852 .take_while(|c| c.is_whitespace() && *c != '\n')
1853 .map(|c| c.len_utf8())
1854 .sum::<usize>();
1855
1856 insert_extra_newline = language.brackets().iter().any(|pair| {
1857 let pair_start = pair.start.trim_end();
1858 let pair_end = pair.end.trim_start();
1859
1860 pair.newline
1861 && buffer
1862 .contains_str_at(end + trailing_whitespace_len, pair_end)
1863 && buffer.contains_str_at(
1864 (start - leading_whitespace_len)
1865 .saturating_sub(pair_start.len()),
1866 pair_start,
1867 )
1868 });
1869 }
1870
1871 let mut new_text = String::with_capacity(1 + indent as usize);
1872 new_text.push('\n');
1873 new_text.extend(iter::repeat(' ').take(indent as usize));
1874 if insert_extra_newline {
1875 new_text = new_text.repeat(2);
1876 }
1877
1878 let anchor = buffer.anchor_after(end);
1879 let new_selection = selection.map(|_| anchor.clone());
1880 (
1881 (start..end, new_text),
1882 (insert_extra_newline, new_selection),
1883 )
1884 })
1885 .unzip()
1886 };
1887
1888 this.buffer.update(cx, |buffer, cx| {
1889 buffer.edit_with_autoindent(edits, cx);
1890 });
1891 let buffer = this.buffer.read(cx).snapshot(cx);
1892 let new_selections = selection_fixup_info
1893 .into_iter()
1894 .map(|(extra_newline_inserted, new_selection)| {
1895 let mut cursor = new_selection.end.to_point(&buffer);
1896 if extra_newline_inserted {
1897 cursor.row -= 1;
1898 cursor.column = buffer.line_len(cursor.row);
1899 }
1900 new_selection.map(|_| cursor.clone())
1901 })
1902 .collect();
1903
1904 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(new_selections));
1905
1906 this.request_autoscroll(Autoscroll::Fit, cx);
1907 });
1908 }
1909
1910 pub fn insert(&mut self, text: &str, cx: &mut ViewContext<Self>) {
1911 let text: Arc<str> = text.into();
1912 self.transact(cx, |this, cx| {
1913 let old_selections = this
1914 .selections
1915 .interleaved::<usize>(&this.buffer.read(cx).snapshot(cx));
1916 let selection_anchors = this.buffer.update(cx, |buffer, cx| {
1917 let anchors = {
1918 let snapshot = buffer.read(cx);
1919 old_selections
1920 .iter()
1921 .map(|s| (s.id, s.goal, snapshot.anchor_after(s.end)))
1922 .collect::<Vec<_>>()
1923 };
1924 buffer.edit_with_autoindent(
1925 old_selections
1926 .iter()
1927 .map(|s| (s.start..s.end, text.clone())),
1928 cx,
1929 );
1930 anchors
1931 });
1932
1933 let selections = {
1934 let snapshot = this.buffer.read(cx).read(cx);
1935 selection_anchors
1936 .into_iter()
1937 .map(|(id, goal, position)| {
1938 let position = position.to_offset(&snapshot);
1939 Selection {
1940 id,
1941 start: position,
1942 end: position,
1943 goal,
1944 reversed: false,
1945 }
1946 })
1947 .collect()
1948 };
1949
1950 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
1951 s.select(selections);
1952 })
1953 });
1954 }
1955
1956 fn trigger_completion_on_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
1957 let selection = self.selections.newest_anchor();
1958 if self
1959 .buffer
1960 .read(cx)
1961 .is_completion_trigger(selection.head(), text, cx)
1962 {
1963 self.show_completions(&ShowCompletions, cx);
1964 } else {
1965 self.hide_context_menu(cx);
1966 }
1967 }
1968
1969 fn surround_with_bracket_pair(&mut self, text: &str, cx: &mut ViewContext<Self>) -> bool {
1970 let snapshot = self.buffer.read(cx).snapshot(cx);
1971 if let Some(pair) = snapshot
1972 .language()
1973 .and_then(|language| language.brackets().iter().find(|b| b.start == text))
1974 .cloned()
1975 {
1976 if self
1977 .selections
1978 .interleaved::<usize>(&snapshot)
1979 .iter()
1980 .any(|selection| selection.is_empty())
1981 {
1982 return false;
1983 }
1984
1985 let mut selections = self.selections.disjoint_anchors().to_vec();
1986 for selection in &mut selections {
1987 selection.end = selection.end.bias_left(&snapshot);
1988 }
1989 drop(snapshot);
1990
1991 self.buffer.update(cx, |buffer, cx| {
1992 let pair_start: Arc<str> = pair.start.clone().into();
1993 let pair_end: Arc<str> = pair.end.clone().into();
1994 buffer.edit(
1995 selections
1996 .iter()
1997 .map(|s| (s.start.clone()..s.start.clone(), pair_start.clone()))
1998 .chain(
1999 selections
2000 .iter()
2001 .map(|s| (s.end.clone()..s.end.clone(), pair_end.clone())),
2002 ),
2003 cx,
2004 );
2005 });
2006
2007 let snapshot = self.buffer.read(cx).read(cx);
2008 for selection in &mut selections {
2009 selection.end = selection.end.bias_right(&snapshot);
2010 }
2011 drop(snapshot);
2012
2013 self.change_selections(None, cx, |s| s.select_anchors(selections));
2014 true
2015 } else {
2016 false
2017 }
2018 }
2019
2020 fn autoclose_bracket_pairs(&mut self, cx: &mut ViewContext<Self>) {
2021 let snapshot = self.buffer.read(cx).snapshot(cx);
2022 let selections = self.selections.interleaved::<usize>(&snapshot);
2023 let mut bracket_pair_state = None;
2024 let mut new_selections = None;
2025 self.buffer.update(cx, |buffer, cx| {
2026 let mut snapshot = buffer.snapshot(cx);
2027 let left_biased_selections = selections
2028 .iter()
2029 .map(|selection| Selection {
2030 id: selection.id,
2031 start: snapshot.anchor_before(selection.start),
2032 end: snapshot.anchor_before(selection.end),
2033 reversed: selection.reversed,
2034 goal: selection.goal,
2035 })
2036 .collect::<Vec<_>>();
2037
2038 let autoclose_pair = snapshot.language().and_then(|language| {
2039 let first_selection_start = selections.first().unwrap().start;
2040 let pair = language.brackets().iter().find(|pair| {
2041 snapshot.contains_str_at(
2042 first_selection_start.saturating_sub(pair.start.len()),
2043 &pair.start,
2044 )
2045 });
2046 pair.and_then(|pair| {
2047 let should_autoclose = selections.iter().all(|selection| {
2048 // Ensure all selections are parked at the end of a pair start.
2049 if snapshot.contains_str_at(
2050 selection.start.saturating_sub(pair.start.len()),
2051 &pair.start,
2052 ) {
2053 snapshot
2054 .chars_at(selection.start)
2055 .next()
2056 .map_or(true, |c| language.should_autoclose_before(c))
2057 } else {
2058 false
2059 }
2060 });
2061
2062 if should_autoclose {
2063 Some(pair.clone())
2064 } else {
2065 None
2066 }
2067 })
2068 });
2069
2070 if let Some(pair) = autoclose_pair {
2071 let selection_ranges = selections
2072 .iter()
2073 .map(|selection| {
2074 let start = selection.start.to_offset(&snapshot);
2075 start..start
2076 })
2077 .collect::<SmallVec<[_; 32]>>();
2078
2079 let pair_end: Arc<str> = pair.end.clone().into();
2080 buffer.edit(
2081 selection_ranges
2082 .iter()
2083 .map(|range| (range.clone(), pair_end.clone())),
2084 cx,
2085 );
2086 snapshot = buffer.snapshot(cx);
2087
2088 new_selections = Some(
2089 resolve_multiple::<usize, _>(left_biased_selections.iter(), &snapshot)
2090 .collect::<Vec<_>>(),
2091 );
2092
2093 if pair.end.len() == 1 {
2094 let mut delta = 0;
2095 bracket_pair_state = Some(BracketPairState {
2096 ranges: selections
2097 .iter()
2098 .map(move |selection| {
2099 let offset = selection.start + delta;
2100 delta += 1;
2101 snapshot.anchor_before(offset)..snapshot.anchor_after(offset)
2102 })
2103 .collect(),
2104 pair,
2105 });
2106 }
2107 }
2108 });
2109
2110 if let Some(new_selections) = new_selections {
2111 self.change_selections(None, cx, |s| {
2112 s.select(new_selections);
2113 });
2114 }
2115 if let Some(bracket_pair_state) = bracket_pair_state {
2116 self.autoclose_stack.push(bracket_pair_state);
2117 }
2118 }
2119
2120 fn skip_autoclose_end(&mut self, text: &str, cx: &mut ViewContext<Self>) -> bool {
2121 let buffer = self.buffer.read(cx).snapshot(cx);
2122 let old_selections = self.selections.interleaved::<usize>(&buffer);
2123 let autoclose_pair = if let Some(autoclose_pair) = self.autoclose_stack.last() {
2124 autoclose_pair
2125 } else {
2126 return false;
2127 };
2128 if text != autoclose_pair.pair.end {
2129 return false;
2130 }
2131
2132 debug_assert_eq!(old_selections.len(), autoclose_pair.ranges.len());
2133
2134 if old_selections
2135 .iter()
2136 .zip(autoclose_pair.ranges.iter().map(|r| r.to_offset(&buffer)))
2137 .all(|(selection, autoclose_range)| {
2138 let autoclose_range_end = autoclose_range.end.to_offset(&buffer);
2139 selection.is_empty() && selection.start == autoclose_range_end
2140 })
2141 {
2142 let new_selections = old_selections
2143 .into_iter()
2144 .map(|selection| {
2145 let cursor = selection.start + 1;
2146 Selection {
2147 id: selection.id,
2148 start: cursor,
2149 end: cursor,
2150 reversed: false,
2151 goal: SelectionGoal::None,
2152 }
2153 })
2154 .collect();
2155 self.autoclose_stack.pop();
2156 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
2157 s.select(new_selections);
2158 });
2159 true
2160 } else {
2161 false
2162 }
2163 }
2164
2165 fn completion_query(buffer: &MultiBufferSnapshot, position: impl ToOffset) -> Option<String> {
2166 let offset = position.to_offset(buffer);
2167 let (word_range, kind) = buffer.surrounding_word(offset);
2168 if offset > word_range.start && kind == Some(CharKind::Word) {
2169 Some(
2170 buffer
2171 .text_for_range(word_range.start..offset)
2172 .collect::<String>(),
2173 )
2174 } else {
2175 None
2176 }
2177 }
2178
2179 fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
2180 if self.pending_rename.is_some() {
2181 return;
2182 }
2183
2184 let project = if let Some(project) = self.project.clone() {
2185 project
2186 } else {
2187 return;
2188 };
2189
2190 let position = self.selections.newest_anchor().head();
2191 let (buffer, buffer_position) = if let Some(output) = self
2192 .buffer
2193 .read(cx)
2194 .text_anchor_for_position(position.clone(), cx)
2195 {
2196 output
2197 } else {
2198 return;
2199 };
2200
2201 let query = Self::completion_query(&self.buffer.read(cx).read(cx), position.clone());
2202 let completions = project.update(cx, |project, cx| {
2203 project.completions(&buffer, buffer_position.clone(), cx)
2204 });
2205
2206 let id = post_inc(&mut self.next_completion_id);
2207 let task = cx.spawn_weak(|this, mut cx| {
2208 async move {
2209 let completions = completions.await?;
2210 if completions.is_empty() {
2211 return Ok(());
2212 }
2213
2214 let mut menu = CompletionsMenu {
2215 id,
2216 initial_position: position,
2217 match_candidates: completions
2218 .iter()
2219 .enumerate()
2220 .map(|(id, completion)| {
2221 StringMatchCandidate::new(
2222 id,
2223 completion.label.text[completion.label.filter_range.clone()].into(),
2224 )
2225 })
2226 .collect(),
2227 buffer,
2228 completions: completions.into(),
2229 matches: Vec::new().into(),
2230 selected_item: 0,
2231 list: Default::default(),
2232 };
2233
2234 menu.filter(query.as_deref(), cx.background()).await;
2235
2236 if let Some(this) = this.upgrade(&cx) {
2237 this.update(&mut cx, |this, cx| {
2238 match this.context_menu.as_ref() {
2239 None => {}
2240 Some(ContextMenu::Completions(prev_menu)) => {
2241 if prev_menu.id > menu.id {
2242 return;
2243 }
2244 }
2245 _ => return,
2246 }
2247
2248 this.completion_tasks.retain(|(id, _)| *id > menu.id);
2249 if this.focused {
2250 this.show_context_menu(ContextMenu::Completions(menu), cx);
2251 }
2252
2253 cx.notify();
2254 });
2255 }
2256 Ok::<_, anyhow::Error>(())
2257 }
2258 .log_err()
2259 });
2260 self.completion_tasks.push((id, task));
2261 }
2262
2263 pub fn confirm_completion(
2264 &mut self,
2265 action: &ConfirmCompletion,
2266 cx: &mut ViewContext<Self>,
2267 ) -> Option<Task<Result<()>>> {
2268 use language::ToOffset as _;
2269
2270 let completions_menu = if let ContextMenu::Completions(menu) = self.hide_context_menu(cx)? {
2271 menu
2272 } else {
2273 return None;
2274 };
2275
2276 let mat = completions_menu
2277 .matches
2278 .get(action.item_ix.unwrap_or(completions_menu.selected_item))?;
2279 let buffer_handle = completions_menu.buffer;
2280 let completion = completions_menu.completions.get(mat.candidate_id)?;
2281
2282 let snippet;
2283 let text;
2284 if completion.is_snippet() {
2285 snippet = Some(Snippet::parse(&completion.new_text).log_err()?);
2286 text = snippet.as_ref().unwrap().text.clone();
2287 } else {
2288 snippet = None;
2289 text = completion.new_text.clone();
2290 };
2291 let buffer = buffer_handle.read(cx);
2292 let old_range = completion.old_range.to_offset(&buffer);
2293 let old_text = buffer.text_for_range(old_range.clone()).collect::<String>();
2294
2295 let selections = self
2296 .selections
2297 .interleaved::<usize>(&self.buffer.read(cx).snapshot(cx));
2298 let newest_selection = self.selections.newest_anchor();
2299 if newest_selection.start.buffer_id != Some(buffer_handle.id()) {
2300 return None;
2301 }
2302
2303 let lookbehind = newest_selection
2304 .start
2305 .text_anchor
2306 .to_offset(buffer)
2307 .saturating_sub(old_range.start);
2308 let lookahead = old_range
2309 .end
2310 .saturating_sub(newest_selection.end.text_anchor.to_offset(buffer));
2311 let mut common_prefix_len = old_text
2312 .bytes()
2313 .zip(text.bytes())
2314 .take_while(|(a, b)| a == b)
2315 .count();
2316
2317 let snapshot = self.buffer.read(cx).snapshot(cx);
2318 let mut ranges = Vec::new();
2319 for selection in &selections {
2320 if snapshot.contains_str_at(selection.start.saturating_sub(lookbehind), &old_text) {
2321 let start = selection.start.saturating_sub(lookbehind);
2322 let end = selection.end + lookahead;
2323 ranges.push(start + common_prefix_len..end);
2324 } else {
2325 common_prefix_len = 0;
2326 ranges.clear();
2327 ranges.extend(selections.iter().map(|s| {
2328 if s.id == newest_selection.id {
2329 old_range.clone()
2330 } else {
2331 s.start..s.end
2332 }
2333 }));
2334 break;
2335 }
2336 }
2337 let text = &text[common_prefix_len..];
2338
2339 self.transact(cx, |this, cx| {
2340 if let Some(mut snippet) = snippet {
2341 snippet.text = text.to_string();
2342 for tabstop in snippet.tabstops.iter_mut().flatten() {
2343 tabstop.start -= common_prefix_len as isize;
2344 tabstop.end -= common_prefix_len as isize;
2345 }
2346
2347 this.insert_snippet(&ranges, snippet, cx).log_err();
2348 } else {
2349 this.buffer.update(cx, |buffer, cx| {
2350 buffer
2351 .edit_with_autoindent(ranges.iter().map(|range| (range.clone(), text)), cx);
2352 });
2353 }
2354 });
2355
2356 let project = self.project.clone()?;
2357 let apply_edits = project.update(cx, |project, cx| {
2358 project.apply_additional_edits_for_completion(
2359 buffer_handle,
2360 completion.clone(),
2361 true,
2362 cx,
2363 )
2364 });
2365 Some(cx.foreground().spawn(async move {
2366 apply_edits.await?;
2367 Ok(())
2368 }))
2369 }
2370
2371 pub fn toggle_code_actions(&mut self, action: &ToggleCodeActions, cx: &mut ViewContext<Self>) {
2372 if matches!(
2373 self.context_menu.as_ref(),
2374 Some(ContextMenu::CodeActions(_))
2375 ) {
2376 self.context_menu.take();
2377 cx.notify();
2378 return;
2379 }
2380
2381 let deployed_from_indicator = action.deployed_from_indicator;
2382 let mut task = self.code_actions_task.take();
2383 cx.spawn_weak(|this, mut cx| async move {
2384 while let Some(prev_task) = task {
2385 prev_task.await;
2386 task = this
2387 .upgrade(&cx)
2388 .and_then(|this| this.update(&mut cx, |this, _| this.code_actions_task.take()));
2389 }
2390
2391 if let Some(this) = this.upgrade(&cx) {
2392 this.update(&mut cx, |this, cx| {
2393 if this.focused {
2394 if let Some((buffer, actions)) = this.available_code_actions.clone() {
2395 this.show_context_menu(
2396 ContextMenu::CodeActions(CodeActionsMenu {
2397 buffer,
2398 actions,
2399 selected_item: Default::default(),
2400 list: Default::default(),
2401 deployed_from_indicator,
2402 }),
2403 cx,
2404 );
2405 }
2406 }
2407 })
2408 }
2409 Ok::<_, anyhow::Error>(())
2410 })
2411 .detach_and_log_err(cx);
2412 }
2413
2414 pub fn confirm_code_action(
2415 workspace: &mut Workspace,
2416 action: &ConfirmCodeAction,
2417 cx: &mut ViewContext<Workspace>,
2418 ) -> Option<Task<Result<()>>> {
2419 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
2420 let actions_menu = if let ContextMenu::CodeActions(menu) =
2421 editor.update(cx, |editor, cx| editor.hide_context_menu(cx))?
2422 {
2423 menu
2424 } else {
2425 return None;
2426 };
2427 let action_ix = action.item_ix.unwrap_or(actions_menu.selected_item);
2428 let action = actions_menu.actions.get(action_ix)?.clone();
2429 let title = action.lsp_action.title.clone();
2430 let buffer = actions_menu.buffer;
2431
2432 let apply_code_actions = workspace.project().clone().update(cx, |project, cx| {
2433 project.apply_code_action(buffer, action, true, cx)
2434 });
2435 Some(cx.spawn(|workspace, cx| async move {
2436 let project_transaction = apply_code_actions.await?;
2437 Self::open_project_transaction(editor, workspace, project_transaction, title, cx).await
2438 }))
2439 }
2440
2441 async fn open_project_transaction(
2442 this: ViewHandle<Editor>,
2443 workspace: ViewHandle<Workspace>,
2444 transaction: ProjectTransaction,
2445 title: String,
2446 mut cx: AsyncAppContext,
2447 ) -> Result<()> {
2448 let replica_id = this.read_with(&cx, |this, cx| this.replica_id(cx));
2449
2450 let mut entries = transaction.0.into_iter().collect::<Vec<_>>();
2451 entries.sort_unstable_by_key(|(buffer, _)| {
2452 buffer.read_with(&cx, |buffer, _| buffer.file().map(|f| f.path().clone()))
2453 });
2454
2455 // If the project transaction's edits are all contained within this editor, then
2456 // avoid opening a new editor to display them.
2457
2458 if let Some((buffer, transaction)) = entries.first() {
2459 if entries.len() == 1 {
2460 let excerpt = this.read_with(&cx, |editor, cx| {
2461 editor
2462 .buffer()
2463 .read(cx)
2464 .excerpt_containing(editor.selections.newest_anchor().head(), cx)
2465 });
2466 if let Some((excerpted_buffer, excerpt_range)) = excerpt {
2467 if excerpted_buffer == *buffer {
2468 let snapshot = buffer.read_with(&cx, |buffer, _| buffer.snapshot());
2469 let excerpt_range = excerpt_range.to_offset(&snapshot);
2470 if snapshot
2471 .edited_ranges_for_transaction(transaction)
2472 .all(|range| {
2473 excerpt_range.start <= range.start && excerpt_range.end >= range.end
2474 })
2475 {
2476 return Ok(());
2477 }
2478 }
2479 }
2480 }
2481 } else {
2482 return Ok(());
2483 }
2484
2485 let mut ranges_to_highlight = Vec::new();
2486 let excerpt_buffer = cx.add_model(|cx| {
2487 let mut multibuffer = MultiBuffer::new(replica_id).with_title(title);
2488 for (buffer, transaction) in &entries {
2489 let snapshot = buffer.read(cx).snapshot();
2490 ranges_to_highlight.extend(
2491 multibuffer.push_excerpts_with_context_lines(
2492 buffer.clone(),
2493 snapshot
2494 .edited_ranges_for_transaction::<usize>(transaction)
2495 .collect(),
2496 1,
2497 cx,
2498 ),
2499 );
2500 }
2501 multibuffer.push_transaction(entries.iter().map(|(b, t)| (b, t)));
2502 multibuffer
2503 });
2504
2505 workspace.update(&mut cx, |workspace, cx| {
2506 let project = workspace.project().clone();
2507 let editor =
2508 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
2509 workspace.add_item(Box::new(editor.clone()), cx);
2510 editor.update(cx, |editor, cx| {
2511 editor.highlight_background::<Self>(
2512 ranges_to_highlight,
2513 |theme| theme.editor.highlighted_line_background,
2514 cx,
2515 );
2516 });
2517 });
2518
2519 Ok(())
2520 }
2521
2522 fn refresh_code_actions(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
2523 let project = self.project.as_ref()?;
2524 let buffer = self.buffer.read(cx);
2525 let newest_selection = self.selections.newest_anchor().clone();
2526 let (start_buffer, start) = buffer.text_anchor_for_position(newest_selection.start, cx)?;
2527 let (end_buffer, end) = buffer.text_anchor_for_position(newest_selection.end, cx)?;
2528 if start_buffer != end_buffer {
2529 return None;
2530 }
2531
2532 let actions = project.update(cx, |project, cx| {
2533 project.code_actions(&start_buffer, start..end, cx)
2534 });
2535 self.code_actions_task = Some(cx.spawn_weak(|this, mut cx| async move {
2536 let actions = actions.await;
2537 if let Some(this) = this.upgrade(&cx) {
2538 this.update(&mut cx, |this, cx| {
2539 this.available_code_actions = actions.log_err().and_then(|actions| {
2540 if actions.is_empty() {
2541 None
2542 } else {
2543 Some((start_buffer, actions.into()))
2544 }
2545 });
2546 cx.notify();
2547 })
2548 }
2549 }));
2550 None
2551 }
2552
2553 fn refresh_document_highlights(&mut self, cx: &mut ViewContext<Self>) -> Option<()> {
2554 if self.pending_rename.is_some() {
2555 return None;
2556 }
2557
2558 let project = self.project.as_ref()?;
2559 let buffer = self.buffer.read(cx);
2560 let newest_selection = self.selections.newest_anchor().clone();
2561 let cursor_position = newest_selection.head();
2562 let (cursor_buffer, cursor_buffer_position) =
2563 buffer.text_anchor_for_position(cursor_position.clone(), cx)?;
2564 let (tail_buffer, _) = buffer.text_anchor_for_position(newest_selection.tail(), cx)?;
2565 if cursor_buffer != tail_buffer {
2566 return None;
2567 }
2568
2569 let highlights = project.update(cx, |project, cx| {
2570 project.document_highlights(&cursor_buffer, cursor_buffer_position, cx)
2571 });
2572
2573 self.document_highlights_task = Some(cx.spawn_weak(|this, mut cx| async move {
2574 let highlights = highlights.log_err().await;
2575 if let Some((this, highlights)) = this.upgrade(&cx).zip(highlights) {
2576 this.update(&mut cx, |this, cx| {
2577 if this.pending_rename.is_some() {
2578 return;
2579 }
2580
2581 let buffer_id = cursor_position.buffer_id;
2582 let buffer = this.buffer.read(cx);
2583 if !buffer
2584 .text_anchor_for_position(cursor_position, cx)
2585 .map_or(false, |(buffer, _)| buffer == cursor_buffer)
2586 {
2587 return;
2588 }
2589
2590 let cursor_buffer_snapshot = cursor_buffer.read(cx);
2591 let mut write_ranges = Vec::new();
2592 let mut read_ranges = Vec::new();
2593 for highlight in highlights {
2594 for (excerpt_id, excerpt_range) in
2595 buffer.excerpts_for_buffer(&cursor_buffer, cx)
2596 {
2597 let start = highlight
2598 .range
2599 .start
2600 .max(&excerpt_range.start, cursor_buffer_snapshot);
2601 let end = highlight
2602 .range
2603 .end
2604 .min(&excerpt_range.end, cursor_buffer_snapshot);
2605 if start.cmp(&end, cursor_buffer_snapshot).is_ge() {
2606 continue;
2607 }
2608
2609 let range = Anchor {
2610 buffer_id,
2611 excerpt_id: excerpt_id.clone(),
2612 text_anchor: start,
2613 }..Anchor {
2614 buffer_id,
2615 excerpt_id,
2616 text_anchor: end,
2617 };
2618 if highlight.kind == lsp::DocumentHighlightKind::WRITE {
2619 write_ranges.push(range);
2620 } else {
2621 read_ranges.push(range);
2622 }
2623 }
2624 }
2625
2626 this.highlight_background::<DocumentHighlightRead>(
2627 read_ranges,
2628 |theme| theme.editor.document_highlight_read_background,
2629 cx,
2630 );
2631 this.highlight_background::<DocumentHighlightWrite>(
2632 write_ranges,
2633 |theme| theme.editor.document_highlight_write_background,
2634 cx,
2635 );
2636 cx.notify();
2637 });
2638 }
2639 }));
2640 None
2641 }
2642
2643 pub fn render_code_actions_indicator(
2644 &self,
2645 style: &EditorStyle,
2646 cx: &mut ViewContext<Self>,
2647 ) -> Option<ElementBox> {
2648 if self.available_code_actions.is_some() {
2649 enum Tag {}
2650 Some(
2651 MouseEventHandler::new::<Tag, _, _>(0, cx, |_, _| {
2652 Svg::new("icons/zap.svg")
2653 .with_color(style.code_actions_indicator)
2654 .boxed()
2655 })
2656 .with_cursor_style(CursorStyle::PointingHand)
2657 .with_padding(Padding::uniform(3.))
2658 .on_mouse_down(|cx| {
2659 cx.dispatch_action(ToggleCodeActions {
2660 deployed_from_indicator: true,
2661 });
2662 })
2663 .boxed(),
2664 )
2665 } else {
2666 None
2667 }
2668 }
2669
2670 pub fn context_menu_visible(&self) -> bool {
2671 self.context_menu
2672 .as_ref()
2673 .map_or(false, |menu| menu.visible())
2674 }
2675
2676 pub fn render_context_menu(
2677 &self,
2678 cursor_position: DisplayPoint,
2679 style: EditorStyle,
2680 cx: &AppContext,
2681 ) -> Option<(DisplayPoint, ElementBox)> {
2682 self.context_menu
2683 .as_ref()
2684 .map(|menu| menu.render(cursor_position, style, cx))
2685 }
2686
2687 fn show_context_menu(&mut self, menu: ContextMenu, cx: &mut ViewContext<Self>) {
2688 if !matches!(menu, ContextMenu::Completions(_)) {
2689 self.completion_tasks.clear();
2690 }
2691 self.context_menu = Some(menu);
2692 cx.notify();
2693 }
2694
2695 fn hide_context_menu(&mut self, cx: &mut ViewContext<Self>) -> Option<ContextMenu> {
2696 cx.notify();
2697 self.completion_tasks.clear();
2698 self.context_menu.take()
2699 }
2700
2701 pub fn insert_snippet(
2702 &mut self,
2703 insertion_ranges: &[Range<usize>],
2704 snippet: Snippet,
2705 cx: &mut ViewContext<Self>,
2706 ) -> Result<()> {
2707 let tabstops = self.buffer.update(cx, |buffer, cx| {
2708 let snippet_text: Arc<str> = snippet.text.clone().into();
2709 buffer.edit_with_autoindent(
2710 insertion_ranges
2711 .iter()
2712 .cloned()
2713 .map(|range| (range, snippet_text.clone())),
2714 cx,
2715 );
2716
2717 let snapshot = &*buffer.read(cx);
2718 let snippet = &snippet;
2719 snippet
2720 .tabstops
2721 .iter()
2722 .map(|tabstop| {
2723 let mut tabstop_ranges = tabstop
2724 .iter()
2725 .flat_map(|tabstop_range| {
2726 let mut delta = 0 as isize;
2727 insertion_ranges.iter().map(move |insertion_range| {
2728 let insertion_start = insertion_range.start as isize + delta;
2729 delta +=
2730 snippet.text.len() as isize - insertion_range.len() as isize;
2731
2732 let start = snapshot.anchor_before(
2733 (insertion_start + tabstop_range.start) as usize,
2734 );
2735 let end = snapshot
2736 .anchor_after((insertion_start + tabstop_range.end) as usize);
2737 start..end
2738 })
2739 })
2740 .collect::<Vec<_>>();
2741 tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
2742 tabstop_ranges
2743 })
2744 .collect::<Vec<_>>()
2745 });
2746
2747 if let Some(tabstop) = tabstops.first() {
2748 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
2749 s.select_ranges(tabstop.iter().cloned());
2750 });
2751 self.snippet_stack.push(SnippetState {
2752 active_index: 0,
2753 ranges: tabstops,
2754 });
2755 }
2756
2757 Ok(())
2758 }
2759
2760 pub fn move_to_next_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
2761 self.move_to_snippet_tabstop(Bias::Right, cx)
2762 }
2763
2764 pub fn move_to_prev_snippet_tabstop(&mut self, cx: &mut ViewContext<Self>) -> bool {
2765 self.move_to_snippet_tabstop(Bias::Left, cx)
2766 }
2767
2768 pub fn move_to_snippet_tabstop(&mut self, bias: Bias, cx: &mut ViewContext<Self>) -> bool {
2769 if let Some(mut snippet) = self.snippet_stack.pop() {
2770 match bias {
2771 Bias::Left => {
2772 if snippet.active_index > 0 {
2773 snippet.active_index -= 1;
2774 } else {
2775 self.snippet_stack.push(snippet);
2776 return false;
2777 }
2778 }
2779 Bias::Right => {
2780 if snippet.active_index + 1 < snippet.ranges.len() {
2781 snippet.active_index += 1;
2782 } else {
2783 self.snippet_stack.push(snippet);
2784 return false;
2785 }
2786 }
2787 }
2788 if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
2789 let snapshot = self.buffer.read(cx).snapshot(cx);
2790 let current_ranges_resolved = current_ranges
2791 .iter()
2792 .map(|range| range.start.to_point(&snapshot)..range.end.to_point(&snapshot))
2793 .collect::<Vec<_>>();
2794 dbg!(¤t_ranges, current_ranges_resolved);
2795
2796 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
2797 s.select_anchor_ranges(current_ranges.into_iter().cloned())
2798 });
2799 // If snippet state is not at the last tabstop, push it back on the stack
2800 if snippet.active_index + 1 < snippet.ranges.len() {
2801 self.snippet_stack.push(snippet);
2802 }
2803 return true;
2804 }
2805 }
2806
2807 false
2808 }
2809
2810 pub fn clear(&mut self, cx: &mut ViewContext<Self>) {
2811 self.transact(cx, |this, cx| {
2812 this.select_all(&SelectAll, cx);
2813 this.insert("", cx);
2814 });
2815 }
2816
2817 pub fn backspace(&mut self, _: &Backspace, cx: &mut ViewContext<Self>) {
2818 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2819 let mut selections = self
2820 .selections
2821 .interleaved::<Point>(&display_map.buffer_snapshot);
2822 for selection in &mut selections {
2823 if selection.is_empty() {
2824 let old_head = selection.head();
2825 let mut new_head =
2826 movement::left(&display_map, old_head.to_display_point(&display_map))
2827 .to_point(&display_map);
2828 if let Some((buffer, line_buffer_range)) = display_map
2829 .buffer_snapshot
2830 .buffer_line_for_row(old_head.row)
2831 {
2832 let indent_column = buffer.indent_column_for_line(line_buffer_range.start.row);
2833 let language_name = buffer.language().map(|language| language.name());
2834 let indent = cx.global::<Settings>().tab_size(language_name.as_deref());
2835 if old_head.column <= indent_column && old_head.column > 0 {
2836 new_head = cmp::min(
2837 new_head,
2838 Point::new(old_head.row, ((old_head.column - 1) / indent) * indent),
2839 );
2840 }
2841 }
2842
2843 selection.set_head(new_head, SelectionGoal::None);
2844 }
2845 }
2846
2847 self.transact(cx, |this, cx| {
2848 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
2849 this.insert("", cx);
2850 });
2851 }
2852
2853 pub fn delete(&mut self, _: &Delete, cx: &mut ViewContext<Self>) {
2854 self.transact(cx, |this, cx| {
2855 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
2856 s.move_with(|map, selection| {
2857 if selection.is_empty() {
2858 let cursor = movement::right(map, selection.head());
2859 selection.set_head(cursor, SelectionGoal::None);
2860 }
2861 })
2862 });
2863 this.insert(&"", cx);
2864 });
2865 }
2866
2867 pub fn tab_prev(&mut self, _: &TabPrev, cx: &mut ViewContext<Self>) {
2868 if self.move_to_prev_snippet_tabstop(cx) {
2869 return;
2870 }
2871
2872 self.outdent(&Outdent, cx);
2873 }
2874
2875 pub fn tab(&mut self, _: &Tab, cx: &mut ViewContext<Self>) {
2876 if self.move_to_next_snippet_tabstop(cx) {
2877 return;
2878 }
2879
2880 let mut selections = self
2881 .selections
2882 .interleaved::<Point>(&self.buffer.read(cx).read(cx));
2883 if selections.iter().all(|s| s.is_empty()) {
2884 self.transact(cx, |this, cx| {
2885 this.buffer.update(cx, |buffer, cx| {
2886 for selection in &mut selections {
2887 let language_name =
2888 buffer.language_at(selection.start, cx).map(|l| l.name());
2889 let tab_size = cx.global::<Settings>().tab_size(language_name.as_deref());
2890 let char_column = buffer
2891 .read(cx)
2892 .text_for_range(Point::new(selection.start.row, 0)..selection.start)
2893 .flat_map(str::chars)
2894 .count();
2895 let chars_to_next_tab_stop = tab_size - (char_column as u32 % tab_size);
2896 buffer.edit(
2897 [(
2898 selection.start..selection.start,
2899 " ".repeat(chars_to_next_tab_stop as usize),
2900 )],
2901 cx,
2902 );
2903 selection.start.column += chars_to_next_tab_stop;
2904 selection.end = selection.start;
2905 }
2906 });
2907 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
2908 s.select(selections);
2909 });
2910 });
2911 } else {
2912 self.indent(&Indent, cx);
2913 }
2914 }
2915
2916 pub fn indent(&mut self, _: &Indent, cx: &mut ViewContext<Self>) {
2917 let mut selections = self
2918 .selections
2919 .interleaved::<Point>(&self.buffer.read(cx).read(cx));
2920 self.transact(cx, |this, cx| {
2921 let mut last_indent = None;
2922 this.buffer.update(cx, |buffer, cx| {
2923 let snapshot = buffer.snapshot(cx);
2924 for selection in &mut selections {
2925 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
2926 let tab_size = cx.global::<Settings>().tab_size(language_name.as_deref());
2927 let mut start_row = selection.start.row;
2928 let mut end_row = selection.end.row + 1;
2929
2930 // If a selection ends at the beginning of a line, don't indent
2931 // that last line.
2932 if selection.end.column == 0 {
2933 end_row -= 1;
2934 }
2935
2936 // Avoid re-indenting a row that has already been indented by a
2937 // previous selection, but still update this selection's column
2938 // to reflect that indentation.
2939 if let Some((last_indent_row, last_indent_len)) = last_indent {
2940 if last_indent_row == selection.start.row {
2941 selection.start.column += last_indent_len;
2942 start_row += 1;
2943 }
2944 if last_indent_row == selection.end.row {
2945 selection.end.column += last_indent_len;
2946 }
2947 }
2948
2949 for row in start_row..end_row {
2950 let indent_column = snapshot.indent_column_for_line(row);
2951 let columns_to_next_tab_stop = tab_size - (indent_column % tab_size);
2952 let row_start = Point::new(row, 0);
2953 buffer.edit(
2954 [(
2955 row_start..row_start,
2956 " ".repeat(columns_to_next_tab_stop as usize),
2957 )],
2958 cx,
2959 );
2960
2961 // Update this selection's endpoints to reflect the indentation.
2962 if row == selection.start.row {
2963 selection.start.column += columns_to_next_tab_stop as u32;
2964 }
2965 if row == selection.end.row {
2966 selection.end.column += columns_to_next_tab_stop as u32;
2967 }
2968
2969 last_indent = Some((row, columns_to_next_tab_stop as u32));
2970 }
2971 }
2972 });
2973
2974 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
2975 s.select(selections);
2976 });
2977 });
2978 }
2979
2980 pub fn outdent(&mut self, _: &Outdent, cx: &mut ViewContext<Self>) {
2981 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
2982 let selections = self
2983 .selections
2984 .interleaved::<Point>(&display_map.buffer_snapshot);
2985 let mut deletion_ranges = Vec::new();
2986 let mut last_outdent = None;
2987 {
2988 let buffer = self.buffer.read(cx);
2989 let snapshot = buffer.snapshot(cx);
2990 for selection in &selections {
2991 let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
2992 let tab_size = cx.global::<Settings>().tab_size(language_name.as_deref());
2993 let mut rows = selection.spanned_rows(false, &display_map);
2994
2995 // Avoid re-outdenting a row that has already been outdented by a
2996 // previous selection.
2997 if let Some(last_row) = last_outdent {
2998 if last_row == rows.start {
2999 rows.start += 1;
3000 }
3001 }
3002
3003 for row in rows {
3004 let column = snapshot.indent_column_for_line(row);
3005 if column > 0 {
3006 let mut deletion_len = column % tab_size;
3007 if deletion_len == 0 {
3008 deletion_len = tab_size;
3009 }
3010 deletion_ranges.push(Point::new(row, 0)..Point::new(row, deletion_len));
3011 last_outdent = Some(row);
3012 }
3013 }
3014 }
3015 }
3016
3017 self.transact(cx, |this, cx| {
3018 this.buffer.update(cx, |buffer, cx| {
3019 let empty_str: Arc<str> = "".into();
3020 buffer.edit(
3021 deletion_ranges
3022 .into_iter()
3023 .map(|range| (range, empty_str.clone())),
3024 cx,
3025 );
3026 });
3027 let snapshot = this.buffer.read(cx).snapshot(cx);
3028 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3029 s.select(s.interleaved::<usize>(&snapshot))
3030 });
3031 });
3032 }
3033
3034 pub fn delete_line(&mut self, _: &DeleteLine, cx: &mut ViewContext<Self>) {
3035 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3036 let selections = self
3037 .selections
3038 .interleaved::<Point>(&display_map.buffer_snapshot);
3039
3040 let mut new_cursors = Vec::new();
3041 let mut edit_ranges = Vec::new();
3042 let mut selections = selections.iter().peekable();
3043 while let Some(selection) = selections.next() {
3044 let mut rows = selection.spanned_rows(false, &display_map);
3045 let goal_display_column = selection.head().to_display_point(&display_map).column();
3046
3047 // Accumulate contiguous regions of rows that we want to delete.
3048 while let Some(next_selection) = selections.peek() {
3049 let next_rows = next_selection.spanned_rows(false, &display_map);
3050 if next_rows.start <= rows.end {
3051 rows.end = next_rows.end;
3052 selections.next().unwrap();
3053 } else {
3054 break;
3055 }
3056 }
3057
3058 let buffer = &display_map.buffer_snapshot;
3059 let mut edit_start = Point::new(rows.start, 0).to_offset(&buffer);
3060 let edit_end;
3061 let cursor_buffer_row;
3062 if buffer.max_point().row >= rows.end {
3063 // If there's a line after the range, delete the \n from the end of the row range
3064 // and position the cursor on the next line.
3065 edit_end = Point::new(rows.end, 0).to_offset(&buffer);
3066 cursor_buffer_row = rows.end;
3067 } else {
3068 // If there isn't a line after the range, delete the \n from the line before the
3069 // start of the row range and position the cursor there.
3070 edit_start = edit_start.saturating_sub(1);
3071 edit_end = buffer.len();
3072 cursor_buffer_row = rows.start.saturating_sub(1);
3073 }
3074
3075 let mut cursor = Point::new(cursor_buffer_row, 0).to_display_point(&display_map);
3076 *cursor.column_mut() =
3077 cmp::min(goal_display_column, display_map.line_len(cursor.row()));
3078
3079 new_cursors.push((
3080 selection.id,
3081 buffer.anchor_after(cursor.to_point(&display_map)),
3082 ));
3083 edit_ranges.push(edit_start..edit_end);
3084 }
3085
3086 self.transact(cx, |this, cx| {
3087 let buffer = this.buffer.update(cx, |buffer, cx| {
3088 let empty_str: Arc<str> = "".into();
3089 buffer.edit(
3090 edit_ranges
3091 .into_iter()
3092 .map(|range| (range, empty_str.clone())),
3093 cx,
3094 );
3095 buffer.snapshot(cx)
3096 });
3097 let new_selections = new_cursors
3098 .into_iter()
3099 .map(|(id, cursor)| {
3100 let cursor = cursor.to_point(&buffer);
3101 Selection {
3102 id,
3103 start: cursor,
3104 end: cursor,
3105 reversed: false,
3106 goal: SelectionGoal::None,
3107 }
3108 })
3109 .collect();
3110
3111 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3112 s.select(new_selections);
3113 });
3114 });
3115 }
3116
3117 pub fn duplicate_line(&mut self, _: &DuplicateLine, cx: &mut ViewContext<Self>) {
3118 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3119 let buffer = &display_map.buffer_snapshot;
3120 let selections = self.selections.interleaved::<Point>(buffer);
3121
3122 let mut edits = Vec::new();
3123 let mut selections_iter = selections.iter().peekable();
3124 while let Some(selection) = selections_iter.next() {
3125 // Avoid duplicating the same lines twice.
3126 let mut rows = selection.spanned_rows(false, &display_map);
3127
3128 while let Some(next_selection) = selections_iter.peek() {
3129 let next_rows = next_selection.spanned_rows(false, &display_map);
3130 if next_rows.start <= rows.end - 1 {
3131 rows.end = next_rows.end;
3132 selections_iter.next().unwrap();
3133 } else {
3134 break;
3135 }
3136 }
3137
3138 // Copy the text from the selected row region and splice it at the start of the region.
3139 let start = Point::new(rows.start, 0);
3140 let end = Point::new(rows.end - 1, buffer.line_len(rows.end - 1));
3141 let text = buffer
3142 .text_for_range(start..end)
3143 .chain(Some("\n"))
3144 .collect::<String>();
3145 edits.push((start..start, text));
3146 }
3147
3148 self.transact(cx, |this, cx| {
3149 this.buffer.update(cx, |buffer, cx| {
3150 buffer.edit(edits, cx);
3151 });
3152
3153 this.request_autoscroll(Autoscroll::Fit, cx);
3154 });
3155 }
3156
3157 pub fn move_line_up(&mut self, _: &MoveLineUp, cx: &mut ViewContext<Self>) {
3158 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3159 let buffer = self.buffer.read(cx).snapshot(cx);
3160
3161 let mut edits = Vec::new();
3162 let mut unfold_ranges = Vec::new();
3163 let mut refold_ranges = Vec::new();
3164
3165 let selections = self.selections.interleaved::<Point>(&buffer);
3166 let mut selections = selections.iter().peekable();
3167 let mut contiguous_row_selections = Vec::new();
3168 let mut new_selections = Vec::new();
3169
3170 while let Some(selection) = selections.next() {
3171 // Find all the selections that span a contiguous row range
3172 contiguous_row_selections.push(selection.clone());
3173 let start_row = selection.start.row;
3174 let mut end_row = if selection.end.column > 0 || selection.is_empty() {
3175 display_map.next_line_boundary(selection.end).0.row + 1
3176 } else {
3177 selection.end.row
3178 };
3179
3180 while let Some(next_selection) = selections.peek() {
3181 if next_selection.start.row <= end_row {
3182 end_row = if next_selection.end.column > 0 || next_selection.is_empty() {
3183 display_map.next_line_boundary(next_selection.end).0.row + 1
3184 } else {
3185 next_selection.end.row
3186 };
3187 contiguous_row_selections.push(selections.next().unwrap().clone());
3188 } else {
3189 break;
3190 }
3191 }
3192
3193 // Move the text spanned by the row range to be before the line preceding the row range
3194 if start_row > 0 {
3195 let range_to_move = Point::new(start_row - 1, buffer.line_len(start_row - 1))
3196 ..Point::new(end_row - 1, buffer.line_len(end_row - 1));
3197 let insertion_point = display_map
3198 .prev_line_boundary(Point::new(start_row - 1, 0))
3199 .0;
3200
3201 // Don't move lines across excerpts
3202 if buffer
3203 .excerpt_boundaries_in_range((
3204 Bound::Excluded(insertion_point),
3205 Bound::Included(range_to_move.end),
3206 ))
3207 .next()
3208 .is_none()
3209 {
3210 let text = buffer
3211 .text_for_range(range_to_move.clone())
3212 .flat_map(|s| s.chars())
3213 .skip(1)
3214 .chain(['\n'])
3215 .collect::<String>();
3216
3217 edits.push((
3218 buffer.anchor_after(range_to_move.start)
3219 ..buffer.anchor_before(range_to_move.end),
3220 String::new(),
3221 ));
3222 let insertion_anchor = buffer.anchor_after(insertion_point);
3223 edits.push((insertion_anchor.clone()..insertion_anchor, text));
3224
3225 let row_delta = range_to_move.start.row - insertion_point.row + 1;
3226
3227 // Move selections up
3228 new_selections.extend(contiguous_row_selections.drain(..).map(
3229 |mut selection| {
3230 selection.start.row -= row_delta;
3231 selection.end.row -= row_delta;
3232 selection
3233 },
3234 ));
3235
3236 // Move folds up
3237 unfold_ranges.push(range_to_move.clone());
3238 for fold in display_map.folds_in_range(
3239 buffer.anchor_before(range_to_move.start)
3240 ..buffer.anchor_after(range_to_move.end),
3241 ) {
3242 let mut start = fold.start.to_point(&buffer);
3243 let mut end = fold.end.to_point(&buffer);
3244 start.row -= row_delta;
3245 end.row -= row_delta;
3246 refold_ranges.push(start..end);
3247 }
3248 }
3249 }
3250
3251 // If we didn't move line(s), preserve the existing selections
3252 new_selections.extend(contiguous_row_selections.drain(..));
3253 }
3254
3255 self.transact(cx, |this, cx| {
3256 this.unfold_ranges(unfold_ranges, true, cx);
3257 this.buffer.update(cx, |buffer, cx| {
3258 for (range, text) in edits {
3259 buffer.edit([(range, text)], cx);
3260 }
3261 });
3262 this.fold_ranges(refold_ranges, cx);
3263 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3264 s.select(new_selections);
3265 })
3266 });
3267 }
3268
3269 pub fn move_line_down(&mut self, _: &MoveLineDown, cx: &mut ViewContext<Self>) {
3270 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
3271 let buffer = self.buffer.read(cx).snapshot(cx);
3272
3273 let mut edits = Vec::new();
3274 let mut unfold_ranges = Vec::new();
3275 let mut refold_ranges = Vec::new();
3276
3277 let selections = self.selections.interleaved::<Point>(&buffer);
3278 let mut selections = selections.iter().peekable();
3279 let mut contiguous_row_selections = Vec::new();
3280 let mut new_selections = Vec::new();
3281
3282 while let Some(selection) = selections.next() {
3283 // Find all the selections that span a contiguous row range
3284 contiguous_row_selections.push(selection.clone());
3285 let start_row = selection.start.row;
3286 let mut end_row = if selection.end.column > 0 || selection.is_empty() {
3287 display_map.next_line_boundary(selection.end).0.row + 1
3288 } else {
3289 selection.end.row
3290 };
3291
3292 while let Some(next_selection) = selections.peek() {
3293 if next_selection.start.row <= end_row {
3294 end_row = if next_selection.end.column > 0 || next_selection.is_empty() {
3295 display_map.next_line_boundary(next_selection.end).0.row + 1
3296 } else {
3297 next_selection.end.row
3298 };
3299 contiguous_row_selections.push(selections.next().unwrap().clone());
3300 } else {
3301 break;
3302 }
3303 }
3304
3305 // Move the text spanned by the row range to be after the last line of the row range
3306 if end_row <= buffer.max_point().row {
3307 let range_to_move = Point::new(start_row, 0)..Point::new(end_row, 0);
3308 let insertion_point = display_map.next_line_boundary(Point::new(end_row, 0)).0;
3309
3310 // Don't move lines across excerpt boundaries
3311 if buffer
3312 .excerpt_boundaries_in_range((
3313 Bound::Excluded(range_to_move.start),
3314 Bound::Included(insertion_point),
3315 ))
3316 .next()
3317 .is_none()
3318 {
3319 let mut text = String::from("\n");
3320 text.extend(buffer.text_for_range(range_to_move.clone()));
3321 text.pop(); // Drop trailing newline
3322 edits.push((
3323 buffer.anchor_after(range_to_move.start)
3324 ..buffer.anchor_before(range_to_move.end),
3325 String::new(),
3326 ));
3327 let insertion_anchor = buffer.anchor_after(insertion_point);
3328 edits.push((insertion_anchor.clone()..insertion_anchor, text));
3329
3330 let row_delta = insertion_point.row - range_to_move.end.row + 1;
3331
3332 // Move selections down
3333 new_selections.extend(contiguous_row_selections.drain(..).map(
3334 |mut selection| {
3335 selection.start.row += row_delta;
3336 selection.end.row += row_delta;
3337 selection
3338 },
3339 ));
3340
3341 // Move folds down
3342 unfold_ranges.push(range_to_move.clone());
3343 for fold in display_map.folds_in_range(
3344 buffer.anchor_before(range_to_move.start)
3345 ..buffer.anchor_after(range_to_move.end),
3346 ) {
3347 let mut start = fold.start.to_point(&buffer);
3348 let mut end = fold.end.to_point(&buffer);
3349 start.row += row_delta;
3350 end.row += row_delta;
3351 refold_ranges.push(start..end);
3352 }
3353 }
3354 }
3355
3356 // If we didn't move line(s), preserve the existing selections
3357 new_selections.extend(contiguous_row_selections.drain(..));
3358 }
3359
3360 self.transact(cx, |this, cx| {
3361 this.unfold_ranges(unfold_ranges, true, cx);
3362 this.buffer.update(cx, |buffer, cx| {
3363 for (range, text) in edits {
3364 buffer.edit([(range, text)], cx);
3365 }
3366 });
3367 this.fold_ranges(refold_ranges, cx);
3368 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(new_selections));
3369 });
3370 }
3371
3372 pub fn transpose(&mut self, _: &Transpose, cx: &mut ViewContext<Self>) {
3373 self.transact(cx, |this, cx| {
3374 let edits = this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3375 let mut edits: Vec<(Range<usize>, String)> = Default::default();
3376 s.move_with(|display_map, selection| {
3377 if !selection.is_empty() {
3378 return;
3379 }
3380
3381 let mut head = selection.head();
3382 let mut transpose_offset = head.to_offset(display_map, Bias::Right);
3383 if head.column() == display_map.line_len(head.row()) {
3384 transpose_offset = display_map
3385 .buffer_snapshot
3386 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3387 }
3388
3389 if transpose_offset == 0 {
3390 return;
3391 }
3392
3393 *head.column_mut() += 1;
3394 head = display_map.clip_point(head, Bias::Right);
3395 selection.collapse_to(head, SelectionGoal::Column(head.column()));
3396
3397 let transpose_start = display_map
3398 .buffer_snapshot
3399 .clip_offset(transpose_offset.saturating_sub(1), Bias::Left);
3400 if edits.last().map_or(true, |e| e.0.end <= transpose_start) {
3401 let transpose_end = display_map
3402 .buffer_snapshot
3403 .clip_offset(transpose_offset + 1, Bias::Right);
3404 if let Some(ch) =
3405 display_map.buffer_snapshot.chars_at(transpose_start).next()
3406 {
3407 edits.push((transpose_start..transpose_offset, String::new()));
3408 edits.push((transpose_end..transpose_end, ch.to_string()));
3409 }
3410 }
3411 });
3412 edits
3413 });
3414 this.buffer.update(cx, |buffer, cx| buffer.edit(edits, cx));
3415 let buffer = this.buffer.read(cx).snapshot(cx);
3416 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3417 s.select(s.interleaved::<usize>(&buffer));
3418 });
3419 });
3420 }
3421
3422 pub fn cut(&mut self, _: &Cut, cx: &mut ViewContext<Self>) {
3423 let mut text = String::new();
3424 let buffer = self.buffer.read(cx).snapshot(cx);
3425 let mut selections = self.selections.interleaved::<Point>(&buffer);
3426 let mut clipboard_selections = Vec::with_capacity(selections.len());
3427 {
3428 let max_point = buffer.max_point();
3429 for selection in &mut selections {
3430 let is_entire_line = selection.is_empty();
3431 if is_entire_line {
3432 selection.start = Point::new(selection.start.row, 0);
3433 selection.end = cmp::min(max_point, Point::new(selection.end.row + 1, 0));
3434 selection.goal = SelectionGoal::None;
3435 }
3436 let mut len = 0;
3437 for chunk in buffer.text_for_range(selection.start..selection.end) {
3438 text.push_str(chunk);
3439 len += chunk.len();
3440 }
3441 clipboard_selections.push(ClipboardSelection {
3442 len,
3443 is_entire_line,
3444 });
3445 }
3446 }
3447
3448 self.transact(cx, |this, cx| {
3449 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3450 s.select(selections);
3451 });
3452 this.insert("", cx);
3453 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3454 });
3455 }
3456
3457 pub fn copy(&mut self, _: &Copy, cx: &mut ViewContext<Self>) {
3458 let buffer = self.buffer.read(cx).read(cx);
3459 let selections = self.selections.interleaved::<Point>(&buffer);
3460 let mut text = String::new();
3461 let mut clipboard_selections = Vec::with_capacity(selections.len());
3462 {
3463 let max_point = buffer.max_point();
3464 for selection in selections.iter() {
3465 let mut start = selection.start;
3466 let mut end = selection.end;
3467 let is_entire_line = selection.is_empty();
3468 if is_entire_line {
3469 start = Point::new(start.row, 0);
3470 end = cmp::min(max_point, Point::new(start.row + 1, 0));
3471 }
3472 let mut len = 0;
3473 for chunk in buffer.text_for_range(start..end) {
3474 text.push_str(chunk);
3475 len += chunk.len();
3476 }
3477 clipboard_selections.push(ClipboardSelection {
3478 len,
3479 is_entire_line,
3480 });
3481 }
3482 }
3483
3484 cx.write_to_clipboard(ClipboardItem::new(text).with_metadata(clipboard_selections));
3485 }
3486
3487 pub fn paste(&mut self, _: &Paste, cx: &mut ViewContext<Self>) {
3488 self.transact(cx, |this, cx| {
3489 if let Some(item) = cx.as_mut().read_from_clipboard() {
3490 let mut clipboard_text = Cow::Borrowed(item.text());
3491 if let Some(mut clipboard_selections) = item.metadata::<Vec<ClipboardSelection>>() {
3492 let old_selections = this
3493 .selections
3494 .interleaved::<usize>(&this.buffer.read(cx).read(cx));
3495 let all_selections_were_entire_line =
3496 clipboard_selections.iter().all(|s| s.is_entire_line);
3497 if clipboard_selections.len() != old_selections.len() {
3498 let mut newline_separated_text = String::new();
3499 let mut clipboard_selections = clipboard_selections.drain(..).peekable();
3500 let mut ix = 0;
3501 while let Some(clipboard_selection) = clipboard_selections.next() {
3502 newline_separated_text
3503 .push_str(&clipboard_text[ix..ix + clipboard_selection.len]);
3504 ix += clipboard_selection.len;
3505 if clipboard_selections.peek().is_some() {
3506 newline_separated_text.push('\n');
3507 }
3508 }
3509 clipboard_text = Cow::Owned(newline_separated_text);
3510 }
3511
3512 this.buffer.update(cx, |buffer, cx| {
3513 let snapshot = buffer.read(cx);
3514 let mut start_offset = 0;
3515 let mut edits = Vec::new();
3516 for (ix, selection) in old_selections.iter().enumerate() {
3517 let to_insert;
3518 let entire_line;
3519 if let Some(clipboard_selection) = clipboard_selections.get(ix) {
3520 let end_offset = start_offset + clipboard_selection.len;
3521 to_insert = &clipboard_text[start_offset..end_offset];
3522 entire_line = clipboard_selection.is_entire_line;
3523 start_offset = end_offset;
3524 } else {
3525 to_insert = clipboard_text.as_str();
3526 entire_line = all_selections_were_entire_line;
3527 }
3528
3529 // If the corresponding selection was empty when this slice of the
3530 // clipboard text was written, then the entire line containing the
3531 // selection was copied. If this selection is also currently empty,
3532 // then paste the line before the current line of the buffer.
3533 let range = if selection.is_empty() && entire_line {
3534 let column = selection.start.to_point(&snapshot).column as usize;
3535 let line_start = selection.start - column;
3536 line_start..line_start
3537 } else {
3538 selection.start..selection.end
3539 };
3540
3541 edits.push((range, to_insert));
3542 }
3543 drop(snapshot);
3544 buffer.edit_with_autoindent(edits, cx);
3545 });
3546
3547 let selections = this
3548 .selections
3549 .interleaved::<usize>(&this.buffer.read(cx).read(cx));
3550 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
3551 } else {
3552 this.insert(&clipboard_text, cx);
3553 }
3554 }
3555 });
3556 }
3557
3558 pub fn undo(&mut self, _: &Undo, cx: &mut ViewContext<Self>) {
3559 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.undo(cx)) {
3560 if let Some((selections, _)) = self.selection_history.transaction(tx_id).cloned() {
3561 self.change_selections(None, cx, |s| {
3562 // TODO: move to SelectionsCollection to preserve selection
3563 // invariants without rechecking
3564 s.select_anchors(selections.to_vec());
3565 });
3566 }
3567 self.request_autoscroll(Autoscroll::Fit, cx);
3568 cx.emit(Event::Edited);
3569 }
3570 }
3571
3572 pub fn redo(&mut self, _: &Redo, cx: &mut ViewContext<Self>) {
3573 if let Some(tx_id) = self.buffer.update(cx, |buffer, cx| buffer.redo(cx)) {
3574 if let Some((_, Some(selections))) = self.selection_history.transaction(tx_id).cloned()
3575 {
3576 self.change_selections(None, cx, |s| {
3577 // TODO: move to SelectionsCollection to preserve selection
3578 // invariants without rechecking
3579 s.select_anchors(selections.to_vec());
3580 });
3581 }
3582 self.request_autoscroll(Autoscroll::Fit, cx);
3583 cx.emit(Event::Edited);
3584 }
3585 }
3586
3587 pub fn finalize_last_transaction(&mut self, cx: &mut ViewContext<Self>) {
3588 self.buffer
3589 .update(cx, |buffer, cx| buffer.finalize_last_transaction(cx));
3590 }
3591
3592 pub fn move_left(&mut self, _: &MoveLeft, cx: &mut ViewContext<Self>) {
3593 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3594 s.move_with(|map, selection| {
3595 let cursor = if selection.is_empty() {
3596 movement::left(map, selection.start)
3597 } else {
3598 selection.start
3599 };
3600 selection.collapse_to(cursor, SelectionGoal::None);
3601 });
3602 })
3603 }
3604
3605 pub fn select_left(&mut self, _: &SelectLeft, cx: &mut ViewContext<Self>) {
3606 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3607 s.move_heads_with(|map, head, _| (movement::left(map, head), SelectionGoal::None));
3608 })
3609 }
3610
3611 pub fn move_right(&mut self, _: &MoveRight, cx: &mut ViewContext<Self>) {
3612 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3613 s.move_with(|map, selection| {
3614 let cursor = if selection.is_empty() {
3615 movement::right(map, selection.end)
3616 } else {
3617 selection.end
3618 };
3619 selection.collapse_to(cursor, SelectionGoal::None)
3620 });
3621 })
3622 }
3623
3624 pub fn select_right(&mut self, _: &SelectRight, cx: &mut ViewContext<Self>) {
3625 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3626 s.move_heads_with(|map, head, _| (movement::right(map, head), SelectionGoal::None));
3627 })
3628 }
3629
3630 pub fn move_up(&mut self, _: &MoveUp, cx: &mut ViewContext<Self>) {
3631 if self.take_rename(true, cx).is_some() {
3632 return;
3633 }
3634
3635 if let Some(context_menu) = self.context_menu.as_mut() {
3636 if context_menu.select_prev(cx) {
3637 return;
3638 }
3639 }
3640
3641 if matches!(self.mode, EditorMode::SingleLine) {
3642 cx.propagate_action();
3643 return;
3644 }
3645
3646 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3647 s.move_with(|map, selection| {
3648 if !selection.is_empty() {
3649 selection.goal = SelectionGoal::None;
3650 }
3651 let (cursor, goal) = movement::up(&map, selection.start, selection.goal, false);
3652 selection.collapse_to(cursor, goal);
3653 });
3654 })
3655 }
3656
3657 pub fn select_up(&mut self, _: &SelectUp, cx: &mut ViewContext<Self>) {
3658 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3659 s.move_heads_with(|map, head, goal| movement::up(map, head, goal, false))
3660 })
3661 }
3662
3663 pub fn move_down(&mut self, _: &MoveDown, cx: &mut ViewContext<Self>) {
3664 self.take_rename(true, cx);
3665
3666 if let Some(context_menu) = self.context_menu.as_mut() {
3667 if context_menu.select_next(cx) {
3668 return;
3669 }
3670 }
3671
3672 if matches!(self.mode, EditorMode::SingleLine) {
3673 cx.propagate_action();
3674 return;
3675 }
3676
3677 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3678 s.move_with(|map, selection| {
3679 if !selection.is_empty() {
3680 selection.goal = SelectionGoal::None;
3681 }
3682 let (cursor, goal) = movement::down(&map, selection.end, selection.goal, false);
3683 selection.collapse_to(cursor, goal);
3684 });
3685 });
3686 }
3687
3688 pub fn select_down(&mut self, _: &SelectDown, cx: &mut ViewContext<Self>) {
3689 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3690 s.move_heads_with(|map, head, goal| movement::down(map, head, goal, false))
3691 });
3692 }
3693
3694 pub fn move_to_previous_word_start(
3695 &mut self,
3696 _: &MoveToPreviousWordStart,
3697 cx: &mut ViewContext<Self>,
3698 ) {
3699 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3700 s.move_cursors_with(|map, head, _| {
3701 (
3702 movement::previous_word_start(map, head),
3703 SelectionGoal::None,
3704 )
3705 });
3706 })
3707 }
3708
3709 pub fn move_to_previous_subword_start(
3710 &mut self,
3711 _: &MoveToPreviousSubwordStart,
3712 cx: &mut ViewContext<Self>,
3713 ) {
3714 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3715 s.move_cursors_with(|map, head, _| {
3716 (
3717 movement::previous_subword_start(map, head),
3718 SelectionGoal::None,
3719 )
3720 });
3721 })
3722 }
3723
3724 pub fn select_to_previous_word_start(
3725 &mut self,
3726 _: &SelectToPreviousWordStart,
3727 cx: &mut ViewContext<Self>,
3728 ) {
3729 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3730 s.move_heads_with(|map, head, _| {
3731 (
3732 movement::previous_word_start(map, head),
3733 SelectionGoal::None,
3734 )
3735 });
3736 })
3737 }
3738
3739 pub fn select_to_previous_subword_start(
3740 &mut self,
3741 _: &SelectToPreviousSubwordStart,
3742 cx: &mut ViewContext<Self>,
3743 ) {
3744 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3745 s.move_heads_with(|map, head, _| {
3746 (
3747 movement::previous_subword_start(map, head),
3748 SelectionGoal::None,
3749 )
3750 });
3751 })
3752 }
3753
3754 pub fn delete_to_previous_word_start(
3755 &mut self,
3756 _: &DeleteToPreviousWordStart,
3757 cx: &mut ViewContext<Self>,
3758 ) {
3759 self.transact(cx, |this, cx| {
3760 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3761 s.move_with(|map, selection| {
3762 if selection.is_empty() {
3763 let cursor = movement::previous_word_start(map, selection.head());
3764 selection.set_head(cursor, SelectionGoal::None);
3765 }
3766 });
3767 });
3768 this.insert("", cx);
3769 });
3770 }
3771
3772 pub fn delete_to_previous_subword_start(
3773 &mut self,
3774 _: &DeleteToPreviousSubwordStart,
3775 cx: &mut ViewContext<Self>,
3776 ) {
3777 self.transact(cx, |this, cx| {
3778 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3779 s.move_with(|map, selection| {
3780 if selection.is_empty() {
3781 let cursor = movement::previous_subword_start(map, selection.head());
3782 selection.set_head(cursor, SelectionGoal::None);
3783 }
3784 });
3785 });
3786 this.insert("", cx);
3787 });
3788 }
3789
3790 pub fn move_to_next_word_end(&mut self, _: &MoveToNextWordEnd, cx: &mut ViewContext<Self>) {
3791 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3792 s.move_cursors_with(|map, head, _| {
3793 (movement::next_word_end(map, head), SelectionGoal::None)
3794 });
3795 })
3796 }
3797
3798 pub fn move_to_next_subword_end(
3799 &mut self,
3800 _: &MoveToNextSubwordEnd,
3801 cx: &mut ViewContext<Self>,
3802 ) {
3803 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3804 s.move_cursors_with(|map, head, _| {
3805 (movement::next_subword_end(map, head), SelectionGoal::None)
3806 });
3807 })
3808 }
3809
3810 pub fn select_to_next_word_end(&mut self, _: &SelectToNextWordEnd, cx: &mut ViewContext<Self>) {
3811 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3812 s.move_heads_with(|map, head, _| {
3813 (movement::next_word_end(map, head), SelectionGoal::None)
3814 });
3815 })
3816 }
3817
3818 pub fn select_to_next_subword_end(
3819 &mut self,
3820 _: &SelectToNextSubwordEnd,
3821 cx: &mut ViewContext<Self>,
3822 ) {
3823 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3824 s.move_heads_with(|map, head, _| {
3825 (movement::next_subword_end(map, head), SelectionGoal::None)
3826 });
3827 })
3828 }
3829
3830 pub fn delete_to_next_word_end(&mut self, _: &DeleteToNextWordEnd, cx: &mut ViewContext<Self>) {
3831 self.transact(cx, |this, cx| {
3832 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3833 s.move_with(|map, selection| {
3834 if selection.is_empty() {
3835 let cursor = movement::next_word_end(map, selection.head());
3836 selection.set_head(cursor, SelectionGoal::None);
3837 }
3838 });
3839 });
3840 this.insert("", cx);
3841 });
3842 }
3843
3844 pub fn delete_to_next_subword_end(
3845 &mut self,
3846 _: &DeleteToNextSubwordEnd,
3847 cx: &mut ViewContext<Self>,
3848 ) {
3849 self.transact(cx, |this, cx| {
3850 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3851 s.move_with(|map, selection| {
3852 if selection.is_empty() {
3853 let cursor = movement::next_subword_end(map, selection.head());
3854 selection.set_head(cursor, SelectionGoal::None);
3855 }
3856 });
3857 });
3858 this.insert("", cx);
3859 });
3860 }
3861
3862 pub fn move_to_beginning_of_line(
3863 &mut self,
3864 _: &MoveToBeginningOfLine,
3865 cx: &mut ViewContext<Self>,
3866 ) {
3867 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3868 s.move_cursors_with(|map, head, _| {
3869 (
3870 movement::line_beginning(map, head, true),
3871 SelectionGoal::None,
3872 )
3873 });
3874 })
3875 }
3876
3877 pub fn select_to_beginning_of_line(
3878 &mut self,
3879 action: &SelectToBeginningOfLine,
3880 cx: &mut ViewContext<Self>,
3881 ) {
3882 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3883 s.move_heads_with(|map, head, _| {
3884 (
3885 movement::line_beginning(map, head, action.stop_at_soft_wraps),
3886 SelectionGoal::None,
3887 )
3888 });
3889 });
3890 }
3891
3892 pub fn delete_to_beginning_of_line(
3893 &mut self,
3894 _: &DeleteToBeginningOfLine,
3895 cx: &mut ViewContext<Self>,
3896 ) {
3897 self.transact(cx, |this, cx| {
3898 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
3899 s.move_with(|_, selection| {
3900 selection.reversed = true;
3901 });
3902 });
3903
3904 this.select_to_beginning_of_line(
3905 &SelectToBeginningOfLine {
3906 stop_at_soft_wraps: false,
3907 },
3908 cx,
3909 );
3910 this.backspace(&Backspace, cx);
3911 });
3912 }
3913
3914 pub fn move_to_end_of_line(&mut self, _: &MoveToEndOfLine, cx: &mut ViewContext<Self>) {
3915 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3916 s.move_cursors_with(|map, head, _| {
3917 (movement::line_end(map, head, true), SelectionGoal::None)
3918 });
3919 })
3920 }
3921
3922 pub fn select_to_end_of_line(
3923 &mut self,
3924 action: &SelectToEndOfLine,
3925 cx: &mut ViewContext<Self>,
3926 ) {
3927 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3928 s.move_heads_with(|map, head, _| {
3929 (
3930 movement::line_end(map, head, action.stop_at_soft_wraps),
3931 SelectionGoal::None,
3932 )
3933 });
3934 })
3935 }
3936
3937 pub fn delete_to_end_of_line(&mut self, _: &DeleteToEndOfLine, cx: &mut ViewContext<Self>) {
3938 self.transact(cx, |this, cx| {
3939 this.select_to_end_of_line(
3940 &SelectToEndOfLine {
3941 stop_at_soft_wraps: false,
3942 },
3943 cx,
3944 );
3945 this.delete(&Delete, cx);
3946 });
3947 }
3948
3949 pub fn cut_to_end_of_line(&mut self, _: &CutToEndOfLine, cx: &mut ViewContext<Self>) {
3950 self.transact(cx, |this, cx| {
3951 this.select_to_end_of_line(
3952 &SelectToEndOfLine {
3953 stop_at_soft_wraps: false,
3954 },
3955 cx,
3956 );
3957 this.cut(&Cut, cx);
3958 });
3959 }
3960
3961 pub fn move_to_beginning(&mut self, _: &MoveToBeginning, cx: &mut ViewContext<Self>) {
3962 if matches!(self.mode, EditorMode::SingleLine) {
3963 cx.propagate_action();
3964 return;
3965 }
3966
3967 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3968 s.select_ranges(vec![0..0]);
3969 });
3970 }
3971
3972 pub fn select_to_beginning(&mut self, _: &SelectToBeginning, cx: &mut ViewContext<Self>) {
3973 let mut selection = self
3974 .selections
3975 .last::<Point>(&self.buffer.read(cx).read(cx));
3976 selection.set_head(Point::zero(), SelectionGoal::None);
3977
3978 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3979 s.select(vec![selection]);
3980 });
3981 }
3982
3983 pub fn move_to_end(&mut self, _: &MoveToEnd, cx: &mut ViewContext<Self>) {
3984 if matches!(self.mode, EditorMode::SingleLine) {
3985 cx.propagate_action();
3986 return;
3987 }
3988
3989 let cursor = self.buffer.read(cx).read(cx).len();
3990 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
3991 s.select_ranges(vec![cursor..cursor])
3992 });
3993 }
3994
3995 pub fn set_nav_history(&mut self, nav_history: Option<ItemNavHistory>) {
3996 self.nav_history = nav_history;
3997 }
3998
3999 pub fn nav_history(&self) -> Option<&ItemNavHistory> {
4000 self.nav_history.as_ref()
4001 }
4002
4003 fn push_to_nav_history(
4004 &self,
4005 position: Anchor,
4006 new_position: Option<Point>,
4007 cx: &mut ViewContext<Self>,
4008 ) {
4009 if let Some(nav_history) = &self.nav_history {
4010 let buffer = self.buffer.read(cx).read(cx);
4011 let point = position.to_point(&buffer);
4012 let scroll_top_row = self.scroll_top_anchor.to_point(&buffer).row;
4013 drop(buffer);
4014
4015 if let Some(new_position) = new_position {
4016 let row_delta = (new_position.row as i64 - point.row as i64).abs();
4017 if row_delta < MIN_NAVIGATION_HISTORY_ROW_DELTA {
4018 return;
4019 }
4020 }
4021
4022 nav_history.push(Some(NavigationData {
4023 cursor_anchor: position,
4024 cursor_position: point,
4025 scroll_position: self.scroll_position,
4026 scroll_top_anchor: self.scroll_top_anchor.clone(),
4027 scroll_top_row,
4028 }));
4029 }
4030 }
4031
4032 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
4033 let buffer = self.buffer.read(cx).snapshot(cx);
4034 let mut selection = self.selections.first::<usize>(&buffer);
4035 selection.set_head(buffer.len(), SelectionGoal::None);
4036 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4037 s.select(vec![selection]);
4038 });
4039 }
4040
4041 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
4042 let end = self.buffer.read(cx).read(cx).len();
4043 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4044 s.select_ranges(vec![0..end]);
4045 });
4046 }
4047
4048 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
4049 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4050 let mut selections = self
4051 .selections
4052 .interleaved::<Point>(&display_map.buffer_snapshot);
4053 let max_point = display_map.buffer_snapshot.max_point();
4054 for selection in &mut selections {
4055 let rows = selection.spanned_rows(true, &display_map);
4056 selection.start = Point::new(rows.start, 0);
4057 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
4058 selection.reversed = false;
4059 }
4060 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4061 s.select(selections);
4062 });
4063 }
4064
4065 pub fn split_selection_into_lines(
4066 &mut self,
4067 _: &SplitSelectionIntoLines,
4068 cx: &mut ViewContext<Self>,
4069 ) {
4070 let mut to_unfold = Vec::new();
4071 let mut new_selection_ranges = Vec::new();
4072 {
4073 let buffer = self.buffer.read(cx).read(cx);
4074 let selections = self.selections.interleaved::<Point>(&buffer);
4075 for selection in selections {
4076 for row in selection.start.row..selection.end.row {
4077 let cursor = Point::new(row, buffer.line_len(row));
4078 new_selection_ranges.push(cursor..cursor);
4079 }
4080 new_selection_ranges.push(selection.end..selection.end);
4081 to_unfold.push(selection.start..selection.end);
4082 }
4083 }
4084 self.unfold_ranges(to_unfold, true, cx);
4085 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4086 s.select_ranges(new_selection_ranges);
4087 });
4088 }
4089
4090 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
4091 self.add_selection(true, cx);
4092 }
4093
4094 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
4095 self.add_selection(false, cx);
4096 }
4097
4098 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
4099 self.push_to_selection_history();
4100 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4101 let mut selections = self
4102 .selections
4103 .interleaved::<Point>(&display_map.buffer_snapshot);
4104 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
4105 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
4106 let range = oldest_selection.display_range(&display_map).sorted();
4107 let columns = cmp::min(range.start.column(), range.end.column())
4108 ..cmp::max(range.start.column(), range.end.column());
4109
4110 selections.clear();
4111 let mut stack = Vec::new();
4112 for row in range.start.row()..=range.end.row() {
4113 if let Some(selection) = self.build_columnar_selection(
4114 &display_map,
4115 row,
4116 &columns,
4117 oldest_selection.reversed,
4118 ) {
4119 stack.push(selection.id);
4120 selections.push(selection);
4121 }
4122 }
4123
4124 if above {
4125 stack.reverse();
4126 }
4127
4128 AddSelectionsState { above, stack }
4129 });
4130
4131 let last_added_selection = *state.stack.last().unwrap();
4132 let mut new_selections = Vec::new();
4133 if above == state.above {
4134 let end_row = if above {
4135 0
4136 } else {
4137 display_map.max_point().row()
4138 };
4139
4140 'outer: for selection in selections {
4141 if selection.id == last_added_selection {
4142 let range = selection.display_range(&display_map).sorted();
4143 debug_assert_eq!(range.start.row(), range.end.row());
4144 let mut row = range.start.row();
4145 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
4146 {
4147 start..end
4148 } else {
4149 cmp::min(range.start.column(), range.end.column())
4150 ..cmp::max(range.start.column(), range.end.column())
4151 };
4152
4153 while row != end_row {
4154 if above {
4155 row -= 1;
4156 } else {
4157 row += 1;
4158 }
4159
4160 if let Some(new_selection) = self.build_columnar_selection(
4161 &display_map,
4162 row,
4163 &columns,
4164 selection.reversed,
4165 ) {
4166 state.stack.push(new_selection.id);
4167 if above {
4168 new_selections.push(new_selection);
4169 new_selections.push(selection);
4170 } else {
4171 new_selections.push(selection);
4172 new_selections.push(new_selection);
4173 }
4174
4175 continue 'outer;
4176 }
4177 }
4178 }
4179
4180 new_selections.push(selection);
4181 }
4182 } else {
4183 new_selections = selections;
4184 new_selections.retain(|s| s.id != last_added_selection);
4185 state.stack.pop();
4186 }
4187
4188 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4189 s.select(new_selections);
4190 });
4191 if state.stack.len() > 1 {
4192 self.add_selections_state = Some(state);
4193 }
4194 }
4195
4196 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
4197 self.push_to_selection_history();
4198 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4199 let buffer = &display_map.buffer_snapshot;
4200 let mut selections = self.selections.interleaved::<usize>(&buffer);
4201 if let Some(mut select_next_state) = self.select_next_state.take() {
4202 let query = &select_next_state.query;
4203 if !select_next_state.done {
4204 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
4205 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
4206 let mut next_selected_range = None;
4207
4208 let bytes_after_last_selection =
4209 buffer.bytes_in_range(last_selection.end..buffer.len());
4210 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
4211 let query_matches = query
4212 .stream_find_iter(bytes_after_last_selection)
4213 .map(|result| (last_selection.end, result))
4214 .chain(
4215 query
4216 .stream_find_iter(bytes_before_first_selection)
4217 .map(|result| (0, result)),
4218 );
4219 for (start_offset, query_match) in query_matches {
4220 let query_match = query_match.unwrap(); // can only fail due to I/O
4221 let offset_range =
4222 start_offset + query_match.start()..start_offset + query_match.end();
4223 let display_range = offset_range.start.to_display_point(&display_map)
4224 ..offset_range.end.to_display_point(&display_map);
4225
4226 if !select_next_state.wordwise
4227 || (!movement::is_inside_word(&display_map, display_range.start)
4228 && !movement::is_inside_word(&display_map, display_range.end))
4229 {
4230 next_selected_range = Some(offset_range);
4231 break;
4232 }
4233 }
4234
4235 if let Some(next_selected_range) = next_selected_range {
4236 self.unfold_ranges([next_selected_range.clone()], false, cx);
4237 self.change_selections(Some(Autoscroll::Newest), cx, |s| {
4238 if action.replace_newest {
4239 s.delete(s.newest_anchor().id);
4240 }
4241 s.insert_range(next_selected_range);
4242 });
4243 } else {
4244 select_next_state.done = true;
4245 }
4246 }
4247
4248 self.select_next_state = Some(select_next_state);
4249 } else if selections.len() == 1 {
4250 let selection = selections.last_mut().unwrap();
4251 if selection.start == selection.end {
4252 let word_range = movement::surrounding_word(
4253 &display_map,
4254 selection.start.to_display_point(&display_map),
4255 );
4256 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
4257 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
4258 selection.goal = SelectionGoal::None;
4259 selection.reversed = false;
4260
4261 let query = buffer
4262 .text_for_range(selection.start..selection.end)
4263 .collect::<String>();
4264 let select_state = SelectNextState {
4265 query: AhoCorasick::new_auto_configured(&[query]),
4266 wordwise: true,
4267 done: false,
4268 };
4269 self.unfold_ranges([selection.start..selection.end], false, cx);
4270 self.change_selections(Some(Autoscroll::Newest), cx, |s| {
4271 s.select(selections);
4272 });
4273 self.select_next_state = Some(select_state);
4274 } else {
4275 let query = buffer
4276 .text_for_range(selection.start..selection.end)
4277 .collect::<String>();
4278 self.select_next_state = Some(SelectNextState {
4279 query: AhoCorasick::new_auto_configured(&[query]),
4280 wordwise: false,
4281 done: false,
4282 });
4283 self.select_next(action, cx);
4284 }
4285 }
4286 }
4287
4288 pub fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext<Self>) {
4289 self.transact(cx, |this, cx| {
4290 let mut selections = this
4291 .selections
4292 .interleaved::<Point>(&this.buffer.read(cx).snapshot(cx));
4293 let mut all_selection_lines_are_comments = true;
4294 let mut edit_ranges = Vec::new();
4295 let mut last_toggled_row = None;
4296 this.buffer.update(cx, |buffer, cx| {
4297 // TODO: Handle selections that cross excerpts
4298 for selection in &mut selections {
4299 // Get the line comment prefix. Split its trailing whitespace into a separate string,
4300 // as that portion won't be used for detecting if a line is a comment.
4301 let full_comment_prefix: Arc<str> = if let Some(prefix) = buffer
4302 .language_at(selection.start, cx)
4303 .and_then(|l| l.line_comment_prefix())
4304 {
4305 prefix.into()
4306 } else {
4307 return;
4308 };
4309 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
4310 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
4311 edit_ranges.clear();
4312 let snapshot = buffer.snapshot(cx);
4313
4314 let end_row =
4315 if selection.end.row > selection.start.row && selection.end.column == 0 {
4316 selection.end.row
4317 } else {
4318 selection.end.row + 1
4319 };
4320
4321 for row in selection.start.row..end_row {
4322 // If multiple selections contain a given row, avoid processing that
4323 // row more than once.
4324 if last_toggled_row == Some(row) {
4325 continue;
4326 } else {
4327 last_toggled_row = Some(row);
4328 }
4329
4330 if snapshot.is_line_blank(row) {
4331 continue;
4332 }
4333
4334 let start = Point::new(row, snapshot.indent_column_for_line(row));
4335 let mut line_bytes = snapshot
4336 .bytes_in_range(start..snapshot.max_point())
4337 .flatten()
4338 .copied();
4339
4340 // If this line currently begins with the line comment prefix, then record
4341 // the range containing the prefix.
4342 if all_selection_lines_are_comments
4343 && line_bytes
4344 .by_ref()
4345 .take(comment_prefix.len())
4346 .eq(comment_prefix.bytes())
4347 {
4348 // Include any whitespace that matches the comment prefix.
4349 let matching_whitespace_len = line_bytes
4350 .zip(comment_prefix_whitespace.bytes())
4351 .take_while(|(a, b)| a == b)
4352 .count()
4353 as u32;
4354 let end = Point::new(
4355 row,
4356 start.column
4357 + comment_prefix.len() as u32
4358 + matching_whitespace_len,
4359 );
4360 edit_ranges.push(start..end);
4361 }
4362 // If this line does not begin with the line comment prefix, then record
4363 // the position where the prefix should be inserted.
4364 else {
4365 all_selection_lines_are_comments = false;
4366 edit_ranges.push(start..start);
4367 }
4368 }
4369
4370 if !edit_ranges.is_empty() {
4371 if all_selection_lines_are_comments {
4372 let empty_str: Arc<str> = "".into();
4373 buffer.edit(
4374 edit_ranges
4375 .iter()
4376 .cloned()
4377 .map(|range| (range, empty_str.clone())),
4378 cx,
4379 );
4380 } else {
4381 let min_column =
4382 edit_ranges.iter().map(|r| r.start.column).min().unwrap();
4383 let edits = edit_ranges.iter().map(|range| {
4384 let position = Point::new(range.start.row, min_column);
4385 (position..position, full_comment_prefix.clone())
4386 });
4387 buffer.edit(edits, cx);
4388 }
4389 }
4390 }
4391 });
4392
4393 let selections = this
4394 .selections
4395 .interleaved::<usize>(&this.buffer.read(cx).read(cx));
4396 this.change_selections(Some(Autoscroll::Fit), cx, |s| {
4397 s.select(selections);
4398 });
4399 });
4400 }
4401
4402 pub fn select_larger_syntax_node(
4403 &mut self,
4404 _: &SelectLargerSyntaxNode,
4405 cx: &mut ViewContext<Self>,
4406 ) {
4407 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4408 let buffer = self.buffer.read(cx).snapshot(cx);
4409 let old_selections = self
4410 .selections
4411 .interleaved::<usize>(&buffer)
4412 .into_boxed_slice();
4413
4414 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
4415 let mut selected_larger_node = false;
4416 let new_selections = old_selections
4417 .iter()
4418 .map(|selection| {
4419 let old_range = selection.start..selection.end;
4420 let mut new_range = old_range.clone();
4421 while let Some(containing_range) =
4422 buffer.range_for_syntax_ancestor(new_range.clone())
4423 {
4424 new_range = containing_range;
4425 if !display_map.intersects_fold(new_range.start)
4426 && !display_map.intersects_fold(new_range.end)
4427 {
4428 break;
4429 }
4430 }
4431
4432 selected_larger_node |= new_range != old_range;
4433 Selection {
4434 id: selection.id,
4435 start: new_range.start,
4436 end: new_range.end,
4437 goal: SelectionGoal::None,
4438 reversed: selection.reversed,
4439 }
4440 })
4441 .collect::<Vec<_>>();
4442
4443 if selected_larger_node {
4444 stack.push(old_selections);
4445 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4446 s.select(new_selections);
4447 });
4448 }
4449 self.select_larger_syntax_node_stack = stack;
4450 }
4451
4452 pub fn select_smaller_syntax_node(
4453 &mut self,
4454 _: &SelectSmallerSyntaxNode,
4455 cx: &mut ViewContext<Self>,
4456 ) {
4457 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
4458 if let Some(selections) = stack.pop() {
4459 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4460 s.select(selections.to_vec());
4461 });
4462 }
4463 self.select_larger_syntax_node_stack = stack;
4464 }
4465
4466 pub fn move_to_enclosing_bracket(
4467 &mut self,
4468 _: &MoveToEnclosingBracket,
4469 cx: &mut ViewContext<Self>,
4470 ) {
4471 let buffer = self.buffer.read(cx).snapshot(cx);
4472 let mut selections = self.selections.interleaved::<usize>(&buffer);
4473 for selection in &mut selections {
4474 if let Some((open_range, close_range)) =
4475 buffer.enclosing_bracket_ranges(selection.start..selection.end)
4476 {
4477 let close_range = close_range.to_inclusive();
4478 let destination = if close_range.contains(&selection.start)
4479 && close_range.contains(&selection.end)
4480 {
4481 open_range.end
4482 } else {
4483 *close_range.start()
4484 };
4485 selection.start = destination;
4486 selection.end = destination;
4487 }
4488 }
4489
4490 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4491 s.select(selections);
4492 });
4493 }
4494
4495 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
4496 self.end_selection(cx);
4497 self.selection_history.mode = SelectionHistoryMode::Undoing;
4498 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
4499 self.change_selections(None, cx, |s| {
4500 // TODO: Move to selections so selections invariants can be preserved rather than
4501 // rechecking them.
4502 s.select_anchors(entry.selections.to_vec())
4503 });
4504 self.select_next_state = entry.select_next_state;
4505 self.add_selections_state = entry.add_selections_state;
4506 self.request_autoscroll(Autoscroll::Newest, cx);
4507 }
4508 self.selection_history.mode = SelectionHistoryMode::Normal;
4509 }
4510
4511 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
4512 self.end_selection(cx);
4513 self.selection_history.mode = SelectionHistoryMode::Redoing;
4514 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
4515 self.change_selections(None, cx, |s| {
4516 // TODO: Move to selections so selections invariants can be preserved rather than
4517 // rechecking them.
4518 s.select_anchors(entry.selections.to_vec())
4519 });
4520 self.select_next_state = entry.select_next_state;
4521 self.add_selections_state = entry.add_selections_state;
4522 self.request_autoscroll(Autoscroll::Newest, cx);
4523 }
4524 self.selection_history.mode = SelectionHistoryMode::Normal;
4525 }
4526
4527 fn go_to_next_diagnostic(&mut self, _: &GoToNextDiagnostic, cx: &mut ViewContext<Self>) {
4528 self.go_to_diagnostic(Direction::Next, cx)
4529 }
4530
4531 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
4532 self.go_to_diagnostic(Direction::Prev, cx)
4533 }
4534
4535 pub fn go_to_diagnostic(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
4536 let buffer = self.buffer.read(cx).snapshot(cx);
4537 let selection = self.selections.newest::<usize>(&buffer);
4538 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
4539 active_diagnostics
4540 .primary_range
4541 .to_offset(&buffer)
4542 .to_inclusive()
4543 });
4544 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
4545 if active_primary_range.contains(&selection.head()) {
4546 *active_primary_range.end()
4547 } else {
4548 selection.head()
4549 }
4550 } else {
4551 selection.head()
4552 };
4553
4554 loop {
4555 let mut diagnostics = if direction == Direction::Prev {
4556 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
4557 } else {
4558 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
4559 };
4560 let group = diagnostics.find_map(|entry| {
4561 if entry.diagnostic.is_primary
4562 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
4563 && !entry.range.is_empty()
4564 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
4565 {
4566 Some((entry.range, entry.diagnostic.group_id))
4567 } else {
4568 None
4569 }
4570 });
4571
4572 if let Some((primary_range, group_id)) = group {
4573 self.activate_diagnostics(group_id, cx);
4574 self.change_selections(Some(Autoscroll::Center), cx, |s| {
4575 s.select(vec![Selection {
4576 id: selection.id,
4577 start: primary_range.start,
4578 end: primary_range.start,
4579 reversed: false,
4580 goal: SelectionGoal::None,
4581 }]);
4582 });
4583 break;
4584 } else {
4585 // Cycle around to the start of the buffer, potentially moving back to the start of
4586 // the currently active diagnostic.
4587 active_primary_range.take();
4588 if direction == Direction::Prev {
4589 if search_start == buffer.len() {
4590 break;
4591 } else {
4592 search_start = buffer.len();
4593 }
4594 } else {
4595 if search_start == 0 {
4596 break;
4597 } else {
4598 search_start = 0;
4599 }
4600 }
4601 }
4602 }
4603 }
4604
4605 pub fn go_to_definition(
4606 workspace: &mut Workspace,
4607 _: &GoToDefinition,
4608 cx: &mut ViewContext<Workspace>,
4609 ) {
4610 let active_item = workspace.active_item(cx);
4611 let editor_handle = if let Some(editor) = active_item
4612 .as_ref()
4613 .and_then(|item| item.act_as::<Self>(cx))
4614 {
4615 editor
4616 } else {
4617 return;
4618 };
4619
4620 let editor = editor_handle.read(cx);
4621 let buffer = editor.buffer.read(cx);
4622 let head = editor.selections.newest::<usize>(&buffer.read(cx)).head();
4623 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
4624 text_anchor
4625 } else {
4626 return;
4627 };
4628
4629 let project = workspace.project().clone();
4630 let definitions = project.update(cx, |project, cx| project.definition(&buffer, head, cx));
4631 cx.spawn(|workspace, mut cx| async move {
4632 let definitions = definitions.await?;
4633 workspace.update(&mut cx, |workspace, cx| {
4634 let nav_history = workspace.active_pane().read(cx).nav_history().clone();
4635 for definition in definitions {
4636 let range = definition.range.to_offset(definition.buffer.read(cx));
4637
4638 let target_editor_handle = workspace.open_project_item(definition.buffer, cx);
4639 target_editor_handle.update(cx, |target_editor, cx| {
4640 // When selecting a definition in a different buffer, disable the nav history
4641 // to avoid creating a history entry at the previous cursor location.
4642 if editor_handle != target_editor_handle {
4643 nav_history.borrow_mut().disable();
4644 }
4645 target_editor.change_selections(Some(Autoscroll::Center), cx, |s| {
4646 s.select_ranges([range]);
4647 });
4648
4649 nav_history.borrow_mut().enable();
4650 });
4651 }
4652 });
4653
4654 Ok::<(), anyhow::Error>(())
4655 })
4656 .detach_and_log_err(cx);
4657 }
4658
4659 pub fn find_all_references(
4660 workspace: &mut Workspace,
4661 _: &FindAllReferences,
4662 cx: &mut ViewContext<Workspace>,
4663 ) -> Option<Task<Result<()>>> {
4664 let active_item = workspace.active_item(cx)?;
4665 let editor_handle = active_item.act_as::<Self>(cx)?;
4666
4667 let editor = editor_handle.read(cx);
4668 let buffer = editor.buffer.read(cx);
4669 let head = editor.selections.newest::<usize>(&buffer.read(cx)).head();
4670 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
4671 let replica_id = editor.replica_id(cx);
4672
4673 let project = workspace.project().clone();
4674 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
4675 Some(cx.spawn(|workspace, mut cx| async move {
4676 let mut locations = references.await?;
4677 if locations.is_empty() {
4678 return Ok(());
4679 }
4680
4681 locations.sort_by_key(|location| location.buffer.id());
4682 let mut locations = locations.into_iter().peekable();
4683 let mut ranges_to_highlight = Vec::new();
4684
4685 let excerpt_buffer = cx.add_model(|cx| {
4686 let mut symbol_name = None;
4687 let mut multibuffer = MultiBuffer::new(replica_id);
4688 while let Some(location) = locations.next() {
4689 let buffer = location.buffer.read(cx);
4690 let mut ranges_for_buffer = Vec::new();
4691 let range = location.range.to_offset(buffer);
4692 ranges_for_buffer.push(range.clone());
4693 if symbol_name.is_none() {
4694 symbol_name = Some(buffer.text_for_range(range).collect::<String>());
4695 }
4696
4697 while let Some(next_location) = locations.peek() {
4698 if next_location.buffer == location.buffer {
4699 ranges_for_buffer.push(next_location.range.to_offset(buffer));
4700 locations.next();
4701 } else {
4702 break;
4703 }
4704 }
4705
4706 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
4707 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
4708 location.buffer.clone(),
4709 ranges_for_buffer,
4710 1,
4711 cx,
4712 ));
4713 }
4714 multibuffer.with_title(format!("References to `{}`", symbol_name.unwrap()))
4715 });
4716
4717 workspace.update(&mut cx, |workspace, cx| {
4718 let editor =
4719 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
4720 editor.update(cx, |editor, cx| {
4721 editor.highlight_background::<Self>(
4722 ranges_to_highlight,
4723 |theme| theme.editor.highlighted_line_background,
4724 cx,
4725 );
4726 });
4727 workspace.add_item(Box::new(editor), cx);
4728 });
4729
4730 Ok(())
4731 }))
4732 }
4733
4734 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
4735 use language::ToOffset as _;
4736
4737 let project = self.project.clone()?;
4738 let selection = self.selections.newest_anchor().clone();
4739 let (cursor_buffer, cursor_buffer_position) = self
4740 .buffer
4741 .read(cx)
4742 .text_anchor_for_position(selection.head(), cx)?;
4743 let (tail_buffer, _) = self
4744 .buffer
4745 .read(cx)
4746 .text_anchor_for_position(selection.tail(), cx)?;
4747 if tail_buffer != cursor_buffer {
4748 return None;
4749 }
4750
4751 let snapshot = cursor_buffer.read(cx).snapshot();
4752 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
4753 let prepare_rename = project.update(cx, |project, cx| {
4754 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
4755 });
4756
4757 Some(cx.spawn(|this, mut cx| async move {
4758 let rename_range = if let Some(range) = prepare_rename.await? {
4759 Some(range)
4760 } else {
4761 this.read_with(&cx, |this, cx| {
4762 let buffer = this.buffer.read(cx).snapshot(cx);
4763 let mut buffer_highlights = this
4764 .document_highlights_for_position(selection.head(), &buffer)
4765 .filter(|highlight| {
4766 highlight.start.excerpt_id() == selection.head().excerpt_id()
4767 && highlight.end.excerpt_id() == selection.head().excerpt_id()
4768 });
4769 buffer_highlights
4770 .next()
4771 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
4772 })
4773 };
4774 if let Some(rename_range) = rename_range {
4775 let rename_buffer_range = rename_range.to_offset(&snapshot);
4776 let cursor_offset_in_rename_range =
4777 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
4778
4779 this.update(&mut cx, |this, cx| {
4780 this.take_rename(false, cx);
4781 let style = this.style(cx);
4782 let buffer = this.buffer.read(cx).read(cx);
4783 let cursor_offset = selection.head().to_offset(&buffer);
4784 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
4785 let rename_end = rename_start + rename_buffer_range.len();
4786 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
4787 let mut old_highlight_id = None;
4788 let old_name: Arc<str> = buffer
4789 .chunks(rename_start..rename_end, true)
4790 .map(|chunk| {
4791 if old_highlight_id.is_none() {
4792 old_highlight_id = chunk.syntax_highlight_id;
4793 }
4794 chunk.text
4795 })
4796 .collect::<String>()
4797 .into();
4798
4799 drop(buffer);
4800
4801 // Position the selection in the rename editor so that it matches the current selection.
4802 this.show_local_selections = false;
4803 let rename_editor = cx.add_view(|cx| {
4804 let mut editor = Editor::single_line(None, cx);
4805 if let Some(old_highlight_id) = old_highlight_id {
4806 editor.override_text_style =
4807 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
4808 }
4809 editor
4810 .buffer
4811 .update(cx, |buffer, cx| buffer.edit([(0..0, old_name.clone())], cx));
4812 editor.select_all(&SelectAll, cx);
4813 editor
4814 });
4815
4816 let ranges = this
4817 .clear_background_highlights::<DocumentHighlightWrite>(cx)
4818 .into_iter()
4819 .flat_map(|(_, ranges)| ranges)
4820 .chain(
4821 this.clear_background_highlights::<DocumentHighlightRead>(cx)
4822 .into_iter()
4823 .flat_map(|(_, ranges)| ranges),
4824 )
4825 .collect();
4826
4827 this.highlight_text::<Rename>(
4828 ranges,
4829 HighlightStyle {
4830 fade_out: Some(style.rename_fade),
4831 ..Default::default()
4832 },
4833 cx,
4834 );
4835 cx.focus(&rename_editor);
4836 let block_id = this.insert_blocks(
4837 [BlockProperties {
4838 position: range.start.clone(),
4839 height: 1,
4840 render: Arc::new({
4841 let editor = rename_editor.clone();
4842 move |cx: &BlockContext| {
4843 ChildView::new(editor.clone())
4844 .contained()
4845 .with_padding_left(cx.anchor_x)
4846 .boxed()
4847 }
4848 }),
4849 disposition: BlockDisposition::Below,
4850 }],
4851 cx,
4852 )[0];
4853 this.pending_rename = Some(RenameState {
4854 range,
4855 old_name,
4856 editor: rename_editor,
4857 block_id,
4858 });
4859 });
4860 }
4861
4862 Ok(())
4863 }))
4864 }
4865
4866 pub fn confirm_rename(
4867 workspace: &mut Workspace,
4868 _: &ConfirmRename,
4869 cx: &mut ViewContext<Workspace>,
4870 ) -> Option<Task<Result<()>>> {
4871 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
4872
4873 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
4874 let rename = editor.take_rename(false, cx)?;
4875 let buffer = editor.buffer.read(cx);
4876 let (start_buffer, start) =
4877 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
4878 let (end_buffer, end) =
4879 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
4880 if start_buffer == end_buffer {
4881 let new_name = rename.editor.read(cx).text(cx);
4882 Some((start_buffer, start..end, rename.old_name, new_name))
4883 } else {
4884 None
4885 }
4886 })?;
4887
4888 let rename = workspace.project().clone().update(cx, |project, cx| {
4889 project.perform_rename(
4890 buffer.clone(),
4891 range.start.clone(),
4892 new_name.clone(),
4893 true,
4894 cx,
4895 )
4896 });
4897
4898 Some(cx.spawn(|workspace, mut cx| async move {
4899 let project_transaction = rename.await?;
4900 Self::open_project_transaction(
4901 editor.clone(),
4902 workspace,
4903 project_transaction,
4904 format!("Rename: {} → {}", old_name, new_name),
4905 cx.clone(),
4906 )
4907 .await?;
4908
4909 editor.update(&mut cx, |editor, cx| {
4910 editor.refresh_document_highlights(cx);
4911 });
4912 Ok(())
4913 }))
4914 }
4915
4916 fn take_rename(
4917 &mut self,
4918 moving_cursor: bool,
4919 cx: &mut ViewContext<Self>,
4920 ) -> Option<RenameState> {
4921 let rename = self.pending_rename.take()?;
4922 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
4923 self.clear_text_highlights::<Rename>(cx);
4924 self.show_local_selections = true;
4925
4926 if moving_cursor {
4927 let rename_editor = rename.editor.read(cx);
4928 let rename_buffer = rename_editor.buffer.read(cx);
4929 let cursor_in_rename_editor = rename_editor
4930 .selections
4931 .newest::<usize>(&rename_buffer.read(cx))
4932 .head();
4933
4934 // Update the selection to match the position of the selection inside
4935 // the rename editor.
4936 let snapshot = self.buffer.read(cx).read(cx);
4937 let rename_range = rename.range.to_offset(&snapshot);
4938 let cursor_in_editor = snapshot
4939 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
4940 .min(rename_range.end);
4941 drop(snapshot);
4942
4943 let new_selection = Selection {
4944 id: self.selections.newest_anchor().id,
4945 start: cursor_in_editor,
4946 end: cursor_in_editor,
4947 reversed: false,
4948 goal: SelectionGoal::None,
4949 };
4950 self.change_selections(None, cx, |s| s.select(vec![new_selection]));
4951 }
4952
4953 Some(rename)
4954 }
4955
4956 #[cfg(any(test, feature = "test-support"))]
4957 pub fn pending_rename(&self) -> Option<&RenameState> {
4958 self.pending_rename.as_ref()
4959 }
4960
4961 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
4962 if let Some(project) = self.project.clone() {
4963 self.buffer.update(cx, |multi_buffer, cx| {
4964 project.update(cx, |project, cx| {
4965 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
4966 });
4967 })
4968 }
4969 }
4970
4971 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
4972 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
4973 let buffer = self.buffer.read(cx).snapshot(cx);
4974 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
4975 let is_valid = buffer
4976 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
4977 .any(|entry| {
4978 entry.diagnostic.is_primary
4979 && !entry.range.is_empty()
4980 && entry.range.start == primary_range_start
4981 && entry.diagnostic.message == active_diagnostics.primary_message
4982 });
4983
4984 if is_valid != active_diagnostics.is_valid {
4985 active_diagnostics.is_valid = is_valid;
4986 let mut new_styles = HashMap::default();
4987 for (block_id, diagnostic) in &active_diagnostics.blocks {
4988 new_styles.insert(
4989 *block_id,
4990 diagnostic_block_renderer(diagnostic.clone(), is_valid),
4991 );
4992 }
4993 self.display_map
4994 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
4995 }
4996 }
4997 }
4998
4999 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) {
5000 self.dismiss_diagnostics(cx);
5001 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
5002 let buffer = self.buffer.read(cx).snapshot(cx);
5003
5004 let mut primary_range = None;
5005 let mut primary_message = None;
5006 let mut group_end = Point::zero();
5007 let diagnostic_group = buffer
5008 .diagnostic_group::<Point>(group_id)
5009 .map(|entry| {
5010 if entry.range.end > group_end {
5011 group_end = entry.range.end;
5012 }
5013 if entry.diagnostic.is_primary {
5014 primary_range = Some(entry.range.clone());
5015 primary_message = Some(entry.diagnostic.message.clone());
5016 }
5017 entry
5018 })
5019 .collect::<Vec<_>>();
5020 let primary_range = primary_range.unwrap();
5021 let primary_message = primary_message.unwrap();
5022 let primary_range =
5023 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
5024
5025 let blocks = display_map
5026 .insert_blocks(
5027 diagnostic_group.iter().map(|entry| {
5028 let diagnostic = entry.diagnostic.clone();
5029 let message_height = diagnostic.message.lines().count() as u8;
5030 BlockProperties {
5031 position: buffer.anchor_after(entry.range.start),
5032 height: message_height,
5033 render: diagnostic_block_renderer(diagnostic, true),
5034 disposition: BlockDisposition::Below,
5035 }
5036 }),
5037 cx,
5038 )
5039 .into_iter()
5040 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
5041 .collect();
5042
5043 Some(ActiveDiagnosticGroup {
5044 primary_range,
5045 primary_message,
5046 blocks,
5047 is_valid: true,
5048 })
5049 });
5050 }
5051
5052 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
5053 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
5054 self.display_map.update(cx, |display_map, cx| {
5055 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
5056 });
5057 cx.notify();
5058 }
5059 }
5060
5061 fn build_columnar_selection(
5062 &mut self,
5063 display_map: &DisplaySnapshot,
5064 row: u32,
5065 columns: &Range<u32>,
5066 reversed: bool,
5067 ) -> Option<Selection<Point>> {
5068 let is_empty = columns.start == columns.end;
5069 let line_len = display_map.line_len(row);
5070 if columns.start < line_len || (is_empty && columns.start == line_len) {
5071 let start = DisplayPoint::new(row, columns.start);
5072 let end = DisplayPoint::new(row, cmp::min(columns.end, line_len));
5073 // TODO: Don't expose next_selection_id
5074 Some(Selection {
5075 id: post_inc(&mut self.selections.next_selection_id),
5076 start: start.to_point(display_map),
5077 end: end.to_point(display_map),
5078 reversed,
5079 goal: SelectionGoal::ColumnRange {
5080 start: columns.start,
5081 end: columns.end,
5082 },
5083 })
5084 } else {
5085 None
5086 }
5087 }
5088
5089 pub fn set_selections_from_remote(
5090 &mut self,
5091 selections: Vec<Selection<Anchor>>,
5092 cx: &mut ViewContext<Self>,
5093 ) {
5094 let old_cursor_position = self.selections.newest_anchor().head();
5095 self.selections
5096 .change_with(self.display_map.clone(), self.buffer.clone(), cx, |s| {
5097 s.select_anchors(selections);
5098 });
5099 self.selections_did_change(false, &old_cursor_position, cx);
5100 }
5101
5102 fn push_to_selection_history(&mut self) {
5103 self.selection_history.push(SelectionHistoryEntry {
5104 selections: self.selections.disjoint_anchors().clone(),
5105 select_next_state: self.select_next_state.clone(),
5106 add_selections_state: self.add_selections_state.clone(),
5107 });
5108 }
5109
5110 pub fn request_autoscroll(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5111 self.autoscroll_request = Some((autoscroll, true));
5112 cx.notify();
5113 }
5114
5115 fn request_autoscroll_remotely(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5116 self.autoscroll_request = Some((autoscroll, false));
5117 cx.notify();
5118 }
5119
5120 pub fn transact(
5121 &mut self,
5122 cx: &mut ViewContext<Self>,
5123 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
5124 ) {
5125 self.start_transaction_at(Instant::now(), cx);
5126 update(self, cx);
5127 self.end_transaction_at(Instant::now(), cx);
5128 }
5129
5130 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
5131 self.end_selection(cx);
5132 if let Some(tx_id) = self
5133 .buffer
5134 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
5135 {
5136 self.selection_history
5137 .insert_transaction(tx_id, self.selections.disjoint_anchors().clone());
5138 }
5139 }
5140
5141 fn end_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
5142 if let Some(tx_id) = self
5143 .buffer
5144 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
5145 {
5146 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
5147 *end_selections = Some(self.selections.disjoint_anchors().clone());
5148 } else {
5149 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
5150 }
5151
5152 cx.emit(Event::Edited);
5153 }
5154 }
5155
5156 pub fn page_up(&mut self, _: &PageUp, _: &mut ViewContext<Self>) {
5157 log::info!("Editor::page_up");
5158 }
5159
5160 pub fn page_down(&mut self, _: &PageDown, _: &mut ViewContext<Self>) {
5161 log::info!("Editor::page_down");
5162 }
5163
5164 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
5165 let mut fold_ranges = Vec::new();
5166
5167 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5168 let selections = self
5169 .selections
5170 .interleaved::<Point>(&display_map.buffer_snapshot);
5171 for selection in selections {
5172 let range = selection.display_range(&display_map).sorted();
5173 let buffer_start_row = range.start.to_point(&display_map).row;
5174
5175 for row in (0..=range.end.row()).rev() {
5176 if self.is_line_foldable(&display_map, row) && !display_map.is_line_folded(row) {
5177 let fold_range = self.foldable_range_for_line(&display_map, row);
5178 if fold_range.end.row >= buffer_start_row {
5179 fold_ranges.push(fold_range);
5180 if row <= range.start.row() {
5181 break;
5182 }
5183 }
5184 }
5185 }
5186 }
5187
5188 self.fold_ranges(fold_ranges, cx);
5189 }
5190
5191 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
5192 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5193 let buffer = &display_map.buffer_snapshot;
5194 let selections = self.selections.interleaved::<Point>(buffer);
5195 let ranges = selections
5196 .iter()
5197 .map(|s| {
5198 let range = s.display_range(&display_map).sorted();
5199 let mut start = range.start.to_point(&display_map);
5200 let mut end = range.end.to_point(&display_map);
5201 start.column = 0;
5202 end.column = buffer.line_len(end.row);
5203 start..end
5204 })
5205 .collect::<Vec<_>>();
5206 self.unfold_ranges(ranges, true, cx);
5207 }
5208
5209 fn is_line_foldable(&self, display_map: &DisplaySnapshot, display_row: u32) -> bool {
5210 let max_point = display_map.max_point();
5211 if display_row >= max_point.row() {
5212 false
5213 } else {
5214 let (start_indent, is_blank) = display_map.line_indent(display_row);
5215 if is_blank {
5216 false
5217 } else {
5218 for display_row in display_row + 1..=max_point.row() {
5219 let (indent, is_blank) = display_map.line_indent(display_row);
5220 if !is_blank {
5221 return indent > start_indent;
5222 }
5223 }
5224 false
5225 }
5226 }
5227 }
5228
5229 fn foldable_range_for_line(
5230 &self,
5231 display_map: &DisplaySnapshot,
5232 start_row: u32,
5233 ) -> Range<Point> {
5234 let max_point = display_map.max_point();
5235
5236 let (start_indent, _) = display_map.line_indent(start_row);
5237 let start = DisplayPoint::new(start_row, display_map.line_len(start_row));
5238 let mut end = None;
5239 for row in start_row + 1..=max_point.row() {
5240 let (indent, is_blank) = display_map.line_indent(row);
5241 if !is_blank && indent <= start_indent {
5242 end = Some(DisplayPoint::new(row - 1, display_map.line_len(row - 1)));
5243 break;
5244 }
5245 }
5246
5247 let end = end.unwrap_or(max_point);
5248 return start.to_point(display_map)..end.to_point(display_map);
5249 }
5250
5251 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
5252 let selections = self
5253 .selections
5254 .interleaved::<Point>(&self.buffer.read(cx).read(cx));
5255 let ranges = selections.into_iter().map(|s| s.start..s.end);
5256 self.fold_ranges(ranges, cx);
5257 }
5258
5259 pub fn fold_ranges<T: ToOffset>(
5260 &mut self,
5261 ranges: impl IntoIterator<Item = Range<T>>,
5262 cx: &mut ViewContext<Self>,
5263 ) {
5264 let mut ranges = ranges.into_iter().peekable();
5265 if ranges.peek().is_some() {
5266 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
5267 self.request_autoscroll(Autoscroll::Fit, cx);
5268 cx.notify();
5269 }
5270 }
5271
5272 pub fn unfold_ranges<T: ToOffset>(
5273 &mut self,
5274 ranges: impl IntoIterator<Item = Range<T>>,
5275 inclusive: bool,
5276 cx: &mut ViewContext<Self>,
5277 ) {
5278 let mut ranges = ranges.into_iter().peekable();
5279 if ranges.peek().is_some() {
5280 self.display_map
5281 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
5282 self.request_autoscroll(Autoscroll::Fit, cx);
5283 cx.notify();
5284 }
5285 }
5286
5287 pub fn insert_blocks(
5288 &mut self,
5289 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
5290 cx: &mut ViewContext<Self>,
5291 ) -> Vec<BlockId> {
5292 let blocks = self
5293 .display_map
5294 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
5295 self.request_autoscroll(Autoscroll::Fit, cx);
5296 blocks
5297 }
5298
5299 pub fn replace_blocks(
5300 &mut self,
5301 blocks: HashMap<BlockId, RenderBlock>,
5302 cx: &mut ViewContext<Self>,
5303 ) {
5304 self.display_map
5305 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
5306 self.request_autoscroll(Autoscroll::Fit, cx);
5307 }
5308
5309 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
5310 self.display_map.update(cx, |display_map, cx| {
5311 display_map.remove_blocks(block_ids, cx)
5312 });
5313 }
5314
5315 pub fn longest_row(&self, cx: &mut MutableAppContext) -> u32 {
5316 self.display_map
5317 .update(cx, |map, cx| map.snapshot(cx))
5318 .longest_row()
5319 }
5320
5321 pub fn max_point(&self, cx: &mut MutableAppContext) -> DisplayPoint {
5322 self.display_map
5323 .update(cx, |map, cx| map.snapshot(cx))
5324 .max_point()
5325 }
5326
5327 pub fn text(&self, cx: &AppContext) -> String {
5328 self.buffer.read(cx).read(cx).text()
5329 }
5330
5331 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
5332 self.transact(cx, |this, cx| {
5333 this.buffer
5334 .read(cx)
5335 .as_singleton()
5336 .expect("you can only call set_text on editors for singleton buffers")
5337 .update(cx, |buffer, cx| buffer.set_text(text, cx));
5338 });
5339 }
5340
5341 pub fn display_text(&self, cx: &mut MutableAppContext) -> String {
5342 self.display_map
5343 .update(cx, |map, cx| map.snapshot(cx))
5344 .text()
5345 }
5346
5347 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
5348 let language_name = self
5349 .buffer
5350 .read(cx)
5351 .as_singleton()
5352 .and_then(|singleton_buffer| singleton_buffer.read(cx).language())
5353 .map(|l| l.name());
5354
5355 let settings = cx.global::<Settings>();
5356 let mode = self
5357 .soft_wrap_mode_override
5358 .unwrap_or_else(|| settings.soft_wrap(language_name.as_deref()));
5359 match mode {
5360 settings::SoftWrap::None => SoftWrap::None,
5361 settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
5362 settings::SoftWrap::PreferredLineLength => {
5363 SoftWrap::Column(settings.preferred_line_length(language_name.as_deref()))
5364 }
5365 }
5366 }
5367
5368 pub fn set_soft_wrap_mode(&mut self, mode: settings::SoftWrap, cx: &mut ViewContext<Self>) {
5369 self.soft_wrap_mode_override = Some(mode);
5370 cx.notify();
5371 }
5372
5373 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut MutableAppContext) -> bool {
5374 self.display_map
5375 .update(cx, |map, cx| map.set_wrap_width(width, cx))
5376 }
5377
5378 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
5379 self.highlighted_rows = rows;
5380 }
5381
5382 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
5383 self.highlighted_rows.clone()
5384 }
5385
5386 pub fn highlight_background<T: 'static>(
5387 &mut self,
5388 ranges: Vec<Range<Anchor>>,
5389 color_fetcher: fn(&Theme) -> Color,
5390 cx: &mut ViewContext<Self>,
5391 ) {
5392 self.background_highlights
5393 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
5394 cx.notify();
5395 }
5396
5397 pub fn clear_background_highlights<T: 'static>(
5398 &mut self,
5399 cx: &mut ViewContext<Self>,
5400 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
5401 cx.notify();
5402 self.background_highlights.remove(&TypeId::of::<T>())
5403 }
5404
5405 #[cfg(feature = "test-support")]
5406 pub fn all_background_highlights(
5407 &mut self,
5408 cx: &mut ViewContext<Self>,
5409 ) -> Vec<(Range<DisplayPoint>, Color)> {
5410 let snapshot = self.snapshot(cx);
5411 let buffer = &snapshot.buffer_snapshot;
5412 let start = buffer.anchor_before(0);
5413 let end = buffer.anchor_after(buffer.len());
5414 let theme = cx.global::<Settings>().theme.as_ref();
5415 self.background_highlights_in_range(start..end, &snapshot, theme)
5416 }
5417
5418 fn document_highlights_for_position<'a>(
5419 &'a self,
5420 position: Anchor,
5421 buffer: &'a MultiBufferSnapshot,
5422 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
5423 let read_highlights = self
5424 .background_highlights
5425 .get(&TypeId::of::<DocumentHighlightRead>())
5426 .map(|h| &h.1);
5427 let write_highlights = self
5428 .background_highlights
5429 .get(&TypeId::of::<DocumentHighlightRead>())
5430 .map(|h| &h.1);
5431 let left_position = position.bias_left(buffer);
5432 let right_position = position.bias_right(buffer);
5433 read_highlights
5434 .into_iter()
5435 .chain(write_highlights)
5436 .flat_map(move |ranges| {
5437 let start_ix = match ranges.binary_search_by(|probe| {
5438 let cmp = probe.end.cmp(&left_position, &buffer);
5439 if cmp.is_ge() {
5440 Ordering::Greater
5441 } else {
5442 Ordering::Less
5443 }
5444 }) {
5445 Ok(i) | Err(i) => i,
5446 };
5447
5448 let right_position = right_position.clone();
5449 ranges[start_ix..]
5450 .iter()
5451 .take_while(move |range| range.start.cmp(&right_position, &buffer).is_le())
5452 })
5453 }
5454
5455 pub fn background_highlights_in_range(
5456 &self,
5457 search_range: Range<Anchor>,
5458 display_snapshot: &DisplaySnapshot,
5459 theme: &Theme,
5460 ) -> Vec<(Range<DisplayPoint>, Color)> {
5461 let mut results = Vec::new();
5462 let buffer = &display_snapshot.buffer_snapshot;
5463 for (color_fetcher, ranges) in self.background_highlights.values() {
5464 let color = color_fetcher(theme);
5465 let start_ix = match ranges.binary_search_by(|probe| {
5466 let cmp = probe.end.cmp(&search_range.start, &buffer);
5467 if cmp.is_gt() {
5468 Ordering::Greater
5469 } else {
5470 Ordering::Less
5471 }
5472 }) {
5473 Ok(i) | Err(i) => i,
5474 };
5475 for range in &ranges[start_ix..] {
5476 if range.start.cmp(&search_range.end, &buffer).is_ge() {
5477 break;
5478 }
5479 let start = range
5480 .start
5481 .to_point(buffer)
5482 .to_display_point(display_snapshot);
5483 let end = range
5484 .end
5485 .to_point(buffer)
5486 .to_display_point(display_snapshot);
5487 results.push((start..end, color))
5488 }
5489 }
5490 results
5491 }
5492
5493 pub fn highlight_text<T: 'static>(
5494 &mut self,
5495 ranges: Vec<Range<Anchor>>,
5496 style: HighlightStyle,
5497 cx: &mut ViewContext<Self>,
5498 ) {
5499 self.display_map.update(cx, |map, _| {
5500 map.highlight_text(TypeId::of::<T>(), ranges, style)
5501 });
5502 cx.notify();
5503 }
5504
5505 pub fn clear_text_highlights<T: 'static>(
5506 &mut self,
5507 cx: &mut ViewContext<Self>,
5508 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
5509 cx.notify();
5510 self.display_map
5511 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()))
5512 }
5513
5514 fn next_blink_epoch(&mut self) -> usize {
5515 self.blink_epoch += 1;
5516 self.blink_epoch
5517 }
5518
5519 fn pause_cursor_blinking(&mut self, cx: &mut ViewContext<Self>) {
5520 if !self.focused {
5521 return;
5522 }
5523
5524 self.show_local_cursors = true;
5525 cx.notify();
5526
5527 let epoch = self.next_blink_epoch();
5528 cx.spawn(|this, mut cx| {
5529 let this = this.downgrade();
5530 async move {
5531 Timer::after(CURSOR_BLINK_INTERVAL).await;
5532 if let Some(this) = this.upgrade(&cx) {
5533 this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
5534 }
5535 }
5536 })
5537 .detach();
5538 }
5539
5540 fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5541 if epoch == self.blink_epoch {
5542 self.blinking_paused = false;
5543 self.blink_cursors(epoch, cx);
5544 }
5545 }
5546
5547 fn blink_cursors(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5548 if epoch == self.blink_epoch && self.focused && !self.blinking_paused {
5549 self.show_local_cursors = !self.show_local_cursors;
5550 cx.notify();
5551
5552 let epoch = self.next_blink_epoch();
5553 cx.spawn(|this, mut cx| {
5554 let this = this.downgrade();
5555 async move {
5556 Timer::after(CURSOR_BLINK_INTERVAL).await;
5557 if let Some(this) = this.upgrade(&cx) {
5558 this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx));
5559 }
5560 }
5561 })
5562 .detach();
5563 }
5564 }
5565
5566 pub fn show_local_cursors(&self) -> bool {
5567 self.show_local_cursors && self.focused
5568 }
5569
5570 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
5571 cx.notify();
5572 }
5573
5574 fn on_buffer_event(
5575 &mut self,
5576 _: ModelHandle<MultiBuffer>,
5577 event: &language::Event,
5578 cx: &mut ViewContext<Self>,
5579 ) {
5580 match event {
5581 language::Event::Edited => {
5582 self.refresh_active_diagnostics(cx);
5583 self.refresh_code_actions(cx);
5584 cx.emit(Event::BufferEdited);
5585 }
5586 language::Event::Reparsed => cx.emit(Event::Reparsed),
5587 language::Event::Dirtied => cx.emit(Event::Dirtied),
5588 language::Event::Saved => cx.emit(Event::Saved),
5589 language::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
5590 language::Event::Reloaded => cx.emit(Event::TitleChanged),
5591 language::Event::Closed => cx.emit(Event::Closed),
5592 language::Event::DiagnosticsUpdated => {
5593 self.refresh_active_diagnostics(cx);
5594 }
5595 _ => {}
5596 }
5597 }
5598
5599 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
5600 cx.notify();
5601 }
5602
5603 pub fn set_searchable(&mut self, searchable: bool) {
5604 self.searchable = searchable;
5605 }
5606
5607 pub fn searchable(&self) -> bool {
5608 self.searchable
5609 }
5610
5611 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
5612 let active_item = workspace.active_item(cx);
5613 let editor_handle = if let Some(editor) = active_item
5614 .as_ref()
5615 .and_then(|item| item.act_as::<Self>(cx))
5616 {
5617 editor
5618 } else {
5619 cx.propagate_action();
5620 return;
5621 };
5622
5623 let editor = editor_handle.read(cx);
5624 let buffer = editor.buffer.read(cx);
5625 if buffer.is_singleton() {
5626 cx.propagate_action();
5627 return;
5628 }
5629
5630 let mut new_selections_by_buffer = HashMap::default();
5631 for selection in editor.selections.interleaved::<usize>(&buffer.read(cx)) {
5632 for (buffer, mut range) in
5633 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
5634 {
5635 if selection.reversed {
5636 mem::swap(&mut range.start, &mut range.end);
5637 }
5638 new_selections_by_buffer
5639 .entry(buffer)
5640 .or_insert(Vec::new())
5641 .push(range)
5642 }
5643 }
5644
5645 editor_handle.update(cx, |editor, cx| {
5646 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
5647 });
5648 let nav_history = workspace.active_pane().read(cx).nav_history().clone();
5649 nav_history.borrow_mut().disable();
5650
5651 // We defer the pane interaction because we ourselves are a workspace item
5652 // and activating a new item causes the pane to call a method on us reentrantly,
5653 // which panics if we're on the stack.
5654 cx.defer(move |workspace, cx| {
5655 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
5656 let editor = workspace.open_project_item::<Self>(buffer, cx);
5657 editor.update(cx, |editor, cx| {
5658 editor.change_selections(Some(Autoscroll::Newest), cx, |s| {
5659 s.select_ranges(ranges);
5660 });
5661 });
5662 }
5663
5664 nav_history.borrow_mut().enable();
5665 });
5666 }
5667}
5668
5669impl EditorSnapshot {
5670 pub fn is_focused(&self) -> bool {
5671 self.is_focused
5672 }
5673
5674 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
5675 self.placeholder_text.as_ref()
5676 }
5677
5678 pub fn scroll_position(&self) -> Vector2F {
5679 compute_scroll_position(
5680 &self.display_snapshot,
5681 self.scroll_position,
5682 &self.scroll_top_anchor,
5683 )
5684 }
5685}
5686
5687impl Deref for EditorSnapshot {
5688 type Target = DisplaySnapshot;
5689
5690 fn deref(&self) -> &Self::Target {
5691 &self.display_snapshot
5692 }
5693}
5694
5695fn compute_scroll_position(
5696 snapshot: &DisplaySnapshot,
5697 mut scroll_position: Vector2F,
5698 scroll_top_anchor: &Anchor,
5699) -> Vector2F {
5700 if *scroll_top_anchor != Anchor::min() {
5701 let scroll_top = scroll_top_anchor.to_display_point(snapshot).row() as f32;
5702 scroll_position.set_y(scroll_top + scroll_position.y());
5703 } else {
5704 scroll_position.set_y(0.);
5705 }
5706 scroll_position
5707}
5708
5709#[derive(Copy, Clone, Debug, PartialEq, Eq)]
5710pub enum Event {
5711 Activate,
5712 BufferEdited,
5713 Edited,
5714 Reparsed,
5715 Blurred,
5716 Dirtied,
5717 Saved,
5718 TitleChanged,
5719 SelectionsChanged { local: bool },
5720 ScrollPositionChanged { local: bool },
5721 Closed,
5722}
5723
5724pub struct EditorFocused(pub ViewHandle<Editor>);
5725pub struct EditorBlurred(pub ViewHandle<Editor>);
5726pub struct EditorReleased(pub WeakViewHandle<Editor>);
5727
5728impl Entity for Editor {
5729 type Event = Event;
5730
5731 fn release(&mut self, cx: &mut MutableAppContext) {
5732 cx.emit_global(EditorReleased(self.handle.clone()));
5733 }
5734}
5735
5736impl View for Editor {
5737 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
5738 let style = self.style(cx);
5739 self.display_map.update(cx, |map, cx| {
5740 map.set_font(style.text.font_id, style.text.font_size, cx)
5741 });
5742 EditorElement::new(self.handle.clone(), style.clone(), self.cursor_shape).boxed()
5743 }
5744
5745 fn ui_name() -> &'static str {
5746 "Editor"
5747 }
5748
5749 fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
5750 let focused_event = EditorFocused(cx.handle());
5751 cx.emit_global(focused_event);
5752 if let Some(rename) = self.pending_rename.as_ref() {
5753 cx.focus(&rename.editor);
5754 } else {
5755 self.focused = true;
5756 self.blink_cursors(self.blink_epoch, cx);
5757 self.buffer.update(cx, |buffer, cx| {
5758 buffer.finalize_last_transaction(cx);
5759 if self.leader_replica_id.is_none() {
5760 buffer.set_active_selections(&self.selections.disjoint_anchors(), cx);
5761 }
5762 });
5763 }
5764 }
5765
5766 fn on_blur(&mut self, cx: &mut ViewContext<Self>) {
5767 let blurred_event = EditorBlurred(cx.handle());
5768 cx.emit_global(blurred_event);
5769 self.focused = false;
5770 self.buffer
5771 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
5772 self.hide_context_menu(cx);
5773 cx.emit(Event::Blurred);
5774 cx.notify();
5775 }
5776
5777 fn keymap_context(&self, _: &AppContext) -> gpui::keymap::Context {
5778 let mut context = Self::default_keymap_context();
5779 let mode = match self.mode {
5780 EditorMode::SingleLine => "single_line",
5781 EditorMode::AutoHeight { .. } => "auto_height",
5782 EditorMode::Full => "full",
5783 };
5784 context.map.insert("mode".into(), mode.into());
5785 if self.pending_rename.is_some() {
5786 context.set.insert("renaming".into());
5787 }
5788 match self.context_menu.as_ref() {
5789 Some(ContextMenu::Completions(_)) => {
5790 context.set.insert("showing_completions".into());
5791 }
5792 Some(ContextMenu::CodeActions(_)) => {
5793 context.set.insert("showing_code_actions".into());
5794 }
5795 None => {}
5796 }
5797
5798 for layer in self.keymap_context_layers.values() {
5799 context.extend(layer);
5800 }
5801
5802 context
5803 }
5804}
5805
5806fn build_style(
5807 settings: &Settings,
5808 get_field_editor_theme: Option<GetFieldEditorTheme>,
5809 override_text_style: Option<&OverrideTextStyle>,
5810 cx: &AppContext,
5811) -> EditorStyle {
5812 let font_cache = cx.font_cache();
5813
5814 let mut theme = settings.theme.editor.clone();
5815 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
5816 let field_editor_theme = get_field_editor_theme(&settings.theme);
5817 theme.text_color = field_editor_theme.text.color;
5818 theme.selection = field_editor_theme.selection;
5819 theme.background = field_editor_theme
5820 .container
5821 .background_color
5822 .unwrap_or_default();
5823 EditorStyle {
5824 text: field_editor_theme.text,
5825 placeholder_text: field_editor_theme.placeholder_text,
5826 theme,
5827 }
5828 } else {
5829 let font_family_id = settings.buffer_font_family;
5830 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
5831 let font_properties = Default::default();
5832 let font_id = font_cache
5833 .select_font(font_family_id, &font_properties)
5834 .unwrap();
5835 let font_size = settings.buffer_font_size;
5836 EditorStyle {
5837 text: TextStyle {
5838 color: settings.theme.editor.text_color,
5839 font_family_name,
5840 font_family_id,
5841 font_id,
5842 font_size,
5843 font_properties,
5844 underline: Default::default(),
5845 },
5846 placeholder_text: None,
5847 theme,
5848 }
5849 };
5850
5851 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
5852 if let Some(highlighted) = style
5853 .text
5854 .clone()
5855 .highlight(highlight_style, font_cache)
5856 .log_err()
5857 {
5858 style.text = highlighted;
5859 }
5860 }
5861
5862 style
5863}
5864
5865trait SelectionExt {
5866 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
5867 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
5868 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
5869 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
5870 -> Range<u32>;
5871}
5872
5873impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
5874 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
5875 let start = self.start.to_point(buffer);
5876 let end = self.end.to_point(buffer);
5877 if self.reversed {
5878 end..start
5879 } else {
5880 start..end
5881 }
5882 }
5883
5884 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
5885 let start = self.start.to_offset(buffer);
5886 let end = self.end.to_offset(buffer);
5887 if self.reversed {
5888 end..start
5889 } else {
5890 start..end
5891 }
5892 }
5893
5894 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
5895 let start = self
5896 .start
5897 .to_point(&map.buffer_snapshot)
5898 .to_display_point(map);
5899 let end = self
5900 .end
5901 .to_point(&map.buffer_snapshot)
5902 .to_display_point(map);
5903 if self.reversed {
5904 end..start
5905 } else {
5906 start..end
5907 }
5908 }
5909
5910 fn spanned_rows(
5911 &self,
5912 include_end_if_at_line_start: bool,
5913 map: &DisplaySnapshot,
5914 ) -> Range<u32> {
5915 let start = self.start.to_point(&map.buffer_snapshot);
5916 let mut end = self.end.to_point(&map.buffer_snapshot);
5917 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
5918 end.row -= 1;
5919 }
5920
5921 let buffer_start = map.prev_line_boundary(start).0;
5922 let buffer_end = map.next_line_boundary(end).0;
5923 buffer_start.row..buffer_end.row + 1
5924 }
5925}
5926
5927impl<T: InvalidationRegion> InvalidationStack<T> {
5928 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
5929 where
5930 S: Clone + ToOffset,
5931 {
5932 while let Some(region) = self.last() {
5933 let all_selections_inside_invalidation_ranges =
5934 if selections.len() == region.ranges().len() {
5935 selections
5936 .iter()
5937 .zip(region.ranges().iter().map(|r| r.to_offset(&buffer)))
5938 .all(|(selection, invalidation_range)| {
5939 let head = selection.head().to_offset(&buffer);
5940 invalidation_range.start <= head && invalidation_range.end >= head
5941 })
5942 } else {
5943 false
5944 };
5945
5946 if all_selections_inside_invalidation_ranges {
5947 break;
5948 } else {
5949 self.pop();
5950 }
5951 }
5952 }
5953}
5954
5955impl<T> Default for InvalidationStack<T> {
5956 fn default() -> Self {
5957 Self(Default::default())
5958 }
5959}
5960
5961impl<T> Deref for InvalidationStack<T> {
5962 type Target = Vec<T>;
5963
5964 fn deref(&self) -> &Self::Target {
5965 &self.0
5966 }
5967}
5968
5969impl<T> DerefMut for InvalidationStack<T> {
5970 fn deref_mut(&mut self) -> &mut Self::Target {
5971 &mut self.0
5972 }
5973}
5974
5975impl InvalidationRegion for BracketPairState {
5976 fn ranges(&self) -> &[Range<Anchor>] {
5977 &self.ranges
5978 }
5979}
5980
5981impl InvalidationRegion for SnippetState {
5982 fn ranges(&self) -> &[Range<Anchor>] {
5983 &self.ranges[self.active_index]
5984 }
5985}
5986
5987impl Deref for EditorStyle {
5988 type Target = theme::Editor;
5989
5990 fn deref(&self) -> &Self::Target {
5991 &self.theme
5992 }
5993}
5994
5995pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
5996 let mut highlighted_lines = Vec::new();
5997 for line in diagnostic.message.lines() {
5998 highlighted_lines.push(highlight_diagnostic_message(line));
5999 }
6000
6001 Arc::new(move |cx: &BlockContext| {
6002 let settings = cx.global::<Settings>();
6003 let theme = &settings.theme.editor;
6004 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
6005 let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
6006 Flex::column()
6007 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
6008 Label::new(
6009 line.clone(),
6010 style.message.clone().with_font_size(font_size),
6011 )
6012 .with_highlights(highlights.clone())
6013 .contained()
6014 .with_margin_left(cx.anchor_x)
6015 .boxed()
6016 }))
6017 .aligned()
6018 .left()
6019 .boxed()
6020 })
6021}
6022
6023pub fn highlight_diagnostic_message(message: &str) -> (String, Vec<usize>) {
6024 let mut message_without_backticks = String::new();
6025 let mut prev_offset = 0;
6026 let mut inside_block = false;
6027 let mut highlights = Vec::new();
6028 for (match_ix, (offset, _)) in message
6029 .match_indices('`')
6030 .chain([(message.len(), "")])
6031 .enumerate()
6032 {
6033 message_without_backticks.push_str(&message[prev_offset..offset]);
6034 if inside_block {
6035 highlights.extend(prev_offset - match_ix..offset - match_ix);
6036 }
6037
6038 inside_block = !inside_block;
6039 prev_offset = offset + 1;
6040 }
6041
6042 (message_without_backticks, highlights)
6043}
6044
6045pub fn diagnostic_style(
6046 severity: DiagnosticSeverity,
6047 valid: bool,
6048 theme: &theme::Editor,
6049) -> DiagnosticStyle {
6050 match (severity, valid) {
6051 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
6052 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
6053 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
6054 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
6055 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
6056 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
6057 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
6058 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
6059 _ => theme.invalid_hint_diagnostic.clone(),
6060 }
6061}
6062
6063pub fn combine_syntax_and_fuzzy_match_highlights(
6064 text: &str,
6065 default_style: HighlightStyle,
6066 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
6067 match_indices: &[usize],
6068) -> Vec<(Range<usize>, HighlightStyle)> {
6069 let mut result = Vec::new();
6070 let mut match_indices = match_indices.iter().copied().peekable();
6071
6072 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
6073 {
6074 syntax_highlight.weight = None;
6075
6076 // Add highlights for any fuzzy match characters before the next
6077 // syntax highlight range.
6078 while let Some(&match_index) = match_indices.peek() {
6079 if match_index >= range.start {
6080 break;
6081 }
6082 match_indices.next();
6083 let end_index = char_ix_after(match_index, text);
6084 let mut match_style = default_style;
6085 match_style.weight = Some(fonts::Weight::BOLD);
6086 result.push((match_index..end_index, match_style));
6087 }
6088
6089 if range.start == usize::MAX {
6090 break;
6091 }
6092
6093 // Add highlights for any fuzzy match characters within the
6094 // syntax highlight range.
6095 let mut offset = range.start;
6096 while let Some(&match_index) = match_indices.peek() {
6097 if match_index >= range.end {
6098 break;
6099 }
6100
6101 match_indices.next();
6102 if match_index > offset {
6103 result.push((offset..match_index, syntax_highlight));
6104 }
6105
6106 let mut end_index = char_ix_after(match_index, text);
6107 while let Some(&next_match_index) = match_indices.peek() {
6108 if next_match_index == end_index && next_match_index < range.end {
6109 end_index = char_ix_after(next_match_index, text);
6110 match_indices.next();
6111 } else {
6112 break;
6113 }
6114 }
6115
6116 let mut match_style = syntax_highlight;
6117 match_style.weight = Some(fonts::Weight::BOLD);
6118 result.push((match_index..end_index, match_style));
6119 offset = end_index;
6120 }
6121
6122 if offset < range.end {
6123 result.push((offset..range.end, syntax_highlight));
6124 }
6125 }
6126
6127 fn char_ix_after(ix: usize, text: &str) -> usize {
6128 ix + text[ix..].chars().next().unwrap().len_utf8()
6129 }
6130
6131 result
6132}
6133
6134pub fn styled_runs_for_code_label<'a>(
6135 label: &'a CodeLabel,
6136 syntax_theme: &'a theme::SyntaxTheme,
6137) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
6138 let fade_out = HighlightStyle {
6139 fade_out: Some(0.35),
6140 ..Default::default()
6141 };
6142
6143 let mut prev_end = label.filter_range.end;
6144 label
6145 .runs
6146 .iter()
6147 .enumerate()
6148 .flat_map(move |(ix, (range, highlight_id))| {
6149 let style = if let Some(style) = highlight_id.style(syntax_theme) {
6150 style
6151 } else {
6152 return Default::default();
6153 };
6154 let mut muted_style = style.clone();
6155 muted_style.highlight(fade_out);
6156
6157 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
6158 if range.start >= label.filter_range.end {
6159 if range.start > prev_end {
6160 runs.push((prev_end..range.start, fade_out));
6161 }
6162 runs.push((range.clone(), muted_style));
6163 } else if range.end <= label.filter_range.end {
6164 runs.push((range.clone(), style));
6165 } else {
6166 runs.push((range.start..label.filter_range.end, style));
6167 runs.push((label.filter_range.end..range.end, muted_style));
6168 }
6169 prev_end = cmp::max(prev_end, range.end);
6170
6171 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
6172 runs.push((prev_end..label.text.len(), fade_out));
6173 }
6174
6175 runs
6176 })
6177}
6178
6179#[cfg(test)]
6180mod tests {
6181 use crate::test::{assert_text_with_selections, select_ranges};
6182
6183 use super::*;
6184 use gpui::{
6185 geometry::rect::RectF,
6186 platform::{WindowBounds, WindowOptions},
6187 };
6188 use indoc::indoc;
6189 use language::{FakeLspAdapter, LanguageConfig};
6190 use lsp::FakeLanguageServer;
6191 use project::FakeFs;
6192 use settings::LanguageOverride;
6193 use smol::stream::StreamExt;
6194 use std::{cell::RefCell, rc::Rc, time::Instant};
6195 use text::Point;
6196 use unindent::Unindent;
6197 use util::test::{marked_text_by, marked_text_ranges, marked_text_ranges_by, sample_text};
6198 use workspace::{FollowableItem, ItemHandle};
6199
6200 #[gpui::test]
6201 fn test_edit_events(cx: &mut MutableAppContext) {
6202 cx.set_global(Settings::test(cx));
6203 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6204
6205 let events = Rc::new(RefCell::new(Vec::new()));
6206 let (_, editor1) = cx.add_window(Default::default(), {
6207 let events = events.clone();
6208 |cx| {
6209 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6210 if matches!(event, Event::Edited | Event::BufferEdited | Event::Dirtied) {
6211 events.borrow_mut().push(("editor1", *event));
6212 }
6213 })
6214 .detach();
6215 Editor::for_buffer(buffer.clone(), None, cx)
6216 }
6217 });
6218 let (_, editor2) = cx.add_window(Default::default(), {
6219 let events = events.clone();
6220 |cx| {
6221 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6222 if matches!(event, Event::Edited | Event::BufferEdited | Event::Dirtied) {
6223 events.borrow_mut().push(("editor2", *event));
6224 }
6225 })
6226 .detach();
6227 Editor::for_buffer(buffer.clone(), None, cx)
6228 }
6229 });
6230 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6231
6232 // Mutating editor 1 will emit an `Edited` event only for that editor.
6233 editor1.update(cx, |editor, cx| editor.insert("X", cx));
6234 assert_eq!(
6235 mem::take(&mut *events.borrow_mut()),
6236 [
6237 ("editor1", Event::Edited),
6238 ("editor1", Event::BufferEdited),
6239 ("editor2", Event::BufferEdited),
6240 ("editor1", Event::Dirtied),
6241 ("editor2", Event::Dirtied)
6242 ]
6243 );
6244
6245 // Mutating editor 2 will emit an `Edited` event only for that editor.
6246 editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
6247 assert_eq!(
6248 mem::take(&mut *events.borrow_mut()),
6249 [
6250 ("editor2", Event::Edited),
6251 ("editor1", Event::BufferEdited),
6252 ("editor2", Event::BufferEdited),
6253 ]
6254 );
6255
6256 // Undoing on editor 1 will emit an `Edited` event only for that editor.
6257 editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
6258 assert_eq!(
6259 mem::take(&mut *events.borrow_mut()),
6260 [
6261 ("editor1", Event::Edited),
6262 ("editor1", Event::BufferEdited),
6263 ("editor2", Event::BufferEdited),
6264 ]
6265 );
6266
6267 // Redoing on editor 1 will emit an `Edited` event only for that editor.
6268 editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
6269 assert_eq!(
6270 mem::take(&mut *events.borrow_mut()),
6271 [
6272 ("editor1", Event::Edited),
6273 ("editor1", Event::BufferEdited),
6274 ("editor2", Event::BufferEdited),
6275 ]
6276 );
6277
6278 // Undoing on editor 2 will emit an `Edited` event only for that editor.
6279 editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
6280 assert_eq!(
6281 mem::take(&mut *events.borrow_mut()),
6282 [
6283 ("editor2", Event::Edited),
6284 ("editor1", Event::BufferEdited),
6285 ("editor2", Event::BufferEdited),
6286 ]
6287 );
6288
6289 // Redoing on editor 2 will emit an `Edited` event only for that editor.
6290 editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
6291 assert_eq!(
6292 mem::take(&mut *events.borrow_mut()),
6293 [
6294 ("editor2", Event::Edited),
6295 ("editor1", Event::BufferEdited),
6296 ("editor2", Event::BufferEdited),
6297 ]
6298 );
6299
6300 // No event is emitted when the mutation is a no-op.
6301 editor2.update(cx, |editor, cx| {
6302 editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
6303
6304 editor.backspace(&Backspace, cx);
6305 });
6306 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6307 }
6308
6309 #[gpui::test]
6310 fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
6311 cx.set_global(Settings::test(cx));
6312 let mut now = Instant::now();
6313 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6314 let group_interval = buffer.read(cx).transaction_group_interval();
6315 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6316 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6317
6318 editor.update(cx, |editor, cx| {
6319 editor.start_transaction_at(now, cx);
6320 editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
6321
6322 editor.insert("cd", cx);
6323 editor.end_transaction_at(now, cx);
6324 assert_eq!(editor.text(cx), "12cd56");
6325 assert_eq!(editor.selected_ranges(cx), vec![4..4]);
6326
6327 editor.start_transaction_at(now, cx);
6328 editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
6329 editor.insert("e", cx);
6330 editor.end_transaction_at(now, cx);
6331 assert_eq!(editor.text(cx), "12cde6");
6332 assert_eq!(editor.selected_ranges(cx), vec![5..5]);
6333
6334 now += group_interval + Duration::from_millis(1);
6335 editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
6336
6337 // Simulate an edit in another editor
6338 buffer.update(cx, |buffer, cx| {
6339 buffer.start_transaction_at(now, cx);
6340 buffer.edit([(0..1, "a")], cx);
6341 buffer.edit([(1..1, "b")], cx);
6342 buffer.end_transaction_at(now, cx);
6343 });
6344
6345 assert_eq!(editor.text(cx), "ab2cde6");
6346 assert_eq!(editor.selected_ranges(cx), vec![3..3]);
6347
6348 // Last transaction happened past the group interval in a different editor.
6349 // Undo it individually and don't restore selections.
6350 editor.undo(&Undo, cx);
6351 assert_eq!(editor.text(cx), "12cde6");
6352 assert_eq!(editor.selected_ranges(cx), vec![2..2]);
6353
6354 // First two transactions happened within the group interval in this editor.
6355 // Undo them together and restore selections.
6356 editor.undo(&Undo, cx);
6357 editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
6358 assert_eq!(editor.text(cx), "123456");
6359 assert_eq!(editor.selected_ranges(cx), vec![0..0]);
6360
6361 // Redo the first two transactions together.
6362 editor.redo(&Redo, cx);
6363 assert_eq!(editor.text(cx), "12cde6");
6364 assert_eq!(editor.selected_ranges(cx), vec![5..5]);
6365
6366 // Redo the last transaction on its own.
6367 editor.redo(&Redo, cx);
6368 assert_eq!(editor.text(cx), "ab2cde6");
6369 assert_eq!(editor.selected_ranges(cx), vec![6..6]);
6370
6371 // Test empty transactions.
6372 editor.start_transaction_at(now, cx);
6373 editor.end_transaction_at(now, cx);
6374 editor.undo(&Undo, cx);
6375 assert_eq!(editor.text(cx), "12cde6");
6376 });
6377 }
6378
6379 #[gpui::test]
6380 fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
6381 cx.set_global(Settings::test(cx));
6382
6383 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
6384 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6385 editor.update(cx, |view, cx| {
6386 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6387 });
6388 assert_eq!(
6389 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
6390 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6391 );
6392
6393 editor.update(cx, |view, cx| {
6394 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6395 });
6396
6397 assert_eq!(
6398 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
6399 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6400 );
6401
6402 editor.update(cx, |view, cx| {
6403 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6404 });
6405
6406 assert_eq!(
6407 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
6408 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6409 );
6410
6411 editor.update(cx, |view, cx| {
6412 view.end_selection(cx);
6413 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6414 });
6415
6416 assert_eq!(
6417 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
6418 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6419 );
6420
6421 editor.update(cx, |view, cx| {
6422 view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
6423 view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
6424 });
6425
6426 assert_eq!(
6427 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
6428 [
6429 DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
6430 DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
6431 ]
6432 );
6433
6434 editor.update(cx, |view, cx| {
6435 view.end_selection(cx);
6436 });
6437
6438 assert_eq!(
6439 editor.update(cx, |view, cx| view.selected_display_ranges(cx)),
6440 [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
6441 );
6442 }
6443
6444 #[gpui::test]
6445 fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
6446 cx.set_global(Settings::test(cx));
6447 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6448 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6449
6450 view.update(cx, |view, cx| {
6451 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6452 assert_eq!(
6453 view.selected_display_ranges(cx),
6454 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6455 );
6456 });
6457
6458 view.update(cx, |view, cx| {
6459 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6460 assert_eq!(
6461 view.selected_display_ranges(cx),
6462 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6463 );
6464 });
6465
6466 view.update(cx, |view, cx| {
6467 view.cancel(&Cancel, cx);
6468 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6469 assert_eq!(
6470 view.selected_display_ranges(cx),
6471 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6472 );
6473 });
6474 }
6475
6476 #[gpui::test]
6477 fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
6478 cx.set_global(Settings::test(cx));
6479 use workspace::Item;
6480 let nav_history = Rc::new(RefCell::new(workspace::NavHistory::default()));
6481 let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
6482
6483 cx.add_window(Default::default(), |cx| {
6484 let mut editor = build_editor(buffer.clone(), cx);
6485 editor.nav_history = Some(ItemNavHistory::new(nav_history.clone(), &cx.handle()));
6486
6487 // Move the cursor a small distance.
6488 // Nothing is added to the navigation history.
6489 editor.change_selections(None, cx, |s| {
6490 s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
6491 });
6492 editor.change_selections(None, cx, |s| {
6493 s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
6494 });
6495 assert!(nav_history.borrow_mut().pop_backward().is_none());
6496
6497 // Move the cursor a large distance.
6498 // The history can jump back to the previous position.
6499 editor.change_selections(None, cx, |s| {
6500 s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
6501 });
6502 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6503 editor.navigate(nav_entry.data.unwrap(), cx);
6504 assert_eq!(nav_entry.item.id(), cx.view_id());
6505 assert_eq!(
6506 editor.selected_display_ranges(cx),
6507 &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
6508 );
6509 assert!(nav_history.borrow_mut().pop_backward().is_none());
6510
6511 // Move the cursor a small distance via the mouse.
6512 // Nothing is added to the navigation history.
6513 editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
6514 editor.end_selection(cx);
6515 assert_eq!(
6516 editor.selected_display_ranges(cx),
6517 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6518 );
6519 assert!(nav_history.borrow_mut().pop_backward().is_none());
6520
6521 // Move the cursor a large distance via the mouse.
6522 // The history can jump back to the previous position.
6523 editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
6524 editor.end_selection(cx);
6525 assert_eq!(
6526 editor.selected_display_ranges(cx),
6527 &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
6528 );
6529 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6530 editor.navigate(nav_entry.data.unwrap(), cx);
6531 assert_eq!(nav_entry.item.id(), cx.view_id());
6532 assert_eq!(
6533 editor.selected_display_ranges(cx),
6534 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6535 );
6536 assert!(nav_history.borrow_mut().pop_backward().is_none());
6537
6538 // Set scroll position to check later
6539 editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
6540 let original_scroll_position = editor.scroll_position;
6541 let original_scroll_top_anchor = editor.scroll_top_anchor.clone();
6542
6543 // Jump to the end of the document and adjust scroll
6544 editor.move_to_end(&MoveToEnd, cx);
6545 editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx);
6546 assert_ne!(editor.scroll_position, original_scroll_position);
6547 assert_ne!(editor.scroll_top_anchor, original_scroll_top_anchor);
6548
6549 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6550 editor.navigate(nav_entry.data.unwrap(), cx);
6551 assert_eq!(editor.scroll_position, original_scroll_position);
6552 assert_eq!(editor.scroll_top_anchor, original_scroll_top_anchor);
6553
6554 // Ensure we don't panic when navigation data contains invalid anchors *and* points.
6555 let mut invalid_anchor = editor.scroll_top_anchor.clone();
6556 invalid_anchor.text_anchor.buffer_id = Some(999);
6557 let invalid_point = Point::new(9999, 0);
6558 editor.navigate(
6559 Box::new(NavigationData {
6560 cursor_anchor: invalid_anchor.clone(),
6561 cursor_position: invalid_point,
6562 scroll_top_anchor: invalid_anchor.clone(),
6563 scroll_top_row: invalid_point.row,
6564 scroll_position: Default::default(),
6565 }),
6566 cx,
6567 );
6568 assert_eq!(
6569 editor.selected_display_ranges(cx),
6570 &[editor.max_point(cx)..editor.max_point(cx)]
6571 );
6572 assert_eq!(
6573 editor.scroll_position(cx),
6574 vec2f(0., editor.max_point(cx).row() as f32)
6575 );
6576
6577 editor
6578 });
6579 }
6580
6581 #[gpui::test]
6582 fn test_cancel(cx: &mut gpui::MutableAppContext) {
6583 cx.set_global(Settings::test(cx));
6584 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6585 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6586
6587 view.update(cx, |view, cx| {
6588 view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
6589 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6590 view.end_selection(cx);
6591
6592 view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
6593 view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
6594 view.end_selection(cx);
6595 assert_eq!(
6596 view.selected_display_ranges(cx),
6597 [
6598 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
6599 DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
6600 ]
6601 );
6602 });
6603
6604 view.update(cx, |view, cx| {
6605 view.cancel(&Cancel, cx);
6606 assert_eq!(
6607 view.selected_display_ranges(cx),
6608 [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
6609 );
6610 });
6611
6612 view.update(cx, |view, cx| {
6613 view.cancel(&Cancel, cx);
6614 assert_eq!(
6615 view.selected_display_ranges(cx),
6616 [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
6617 );
6618 });
6619 }
6620
6621 #[gpui::test]
6622 fn test_fold(cx: &mut gpui::MutableAppContext) {
6623 cx.set_global(Settings::test(cx));
6624 let buffer = MultiBuffer::build_simple(
6625 &"
6626 impl Foo {
6627 // Hello!
6628
6629 fn a() {
6630 1
6631 }
6632
6633 fn b() {
6634 2
6635 }
6636
6637 fn c() {
6638 3
6639 }
6640 }
6641 "
6642 .unindent(),
6643 cx,
6644 );
6645 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6646
6647 view.update(cx, |view, cx| {
6648 view.change_selections(None, cx, |s| {
6649 s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
6650 });
6651 view.fold(&Fold, cx);
6652 assert_eq!(
6653 view.display_text(cx),
6654 "
6655 impl Foo {
6656 // Hello!
6657
6658 fn a() {
6659 1
6660 }
6661
6662 fn b() {…
6663 }
6664
6665 fn c() {…
6666 }
6667 }
6668 "
6669 .unindent(),
6670 );
6671
6672 view.fold(&Fold, cx);
6673 assert_eq!(
6674 view.display_text(cx),
6675 "
6676 impl Foo {…
6677 }
6678 "
6679 .unindent(),
6680 );
6681
6682 view.unfold_lines(&UnfoldLines, cx);
6683 assert_eq!(
6684 view.display_text(cx),
6685 "
6686 impl Foo {
6687 // Hello!
6688
6689 fn a() {
6690 1
6691 }
6692
6693 fn b() {…
6694 }
6695
6696 fn c() {…
6697 }
6698 }
6699 "
6700 .unindent(),
6701 );
6702
6703 view.unfold_lines(&UnfoldLines, cx);
6704 assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
6705 });
6706 }
6707
6708 #[gpui::test]
6709 fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
6710 cx.set_global(Settings::test(cx));
6711 let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
6712 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6713
6714 buffer.update(cx, |buffer, cx| {
6715 buffer.edit(
6716 vec![
6717 (Point::new(1, 0)..Point::new(1, 0), "\t"),
6718 (Point::new(1, 1)..Point::new(1, 1), "\t"),
6719 ],
6720 cx,
6721 );
6722 });
6723
6724 view.update(cx, |view, cx| {
6725 assert_eq!(
6726 view.selected_display_ranges(cx),
6727 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6728 );
6729
6730 view.move_down(&MoveDown, cx);
6731 assert_eq!(
6732 view.selected_display_ranges(cx),
6733 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
6734 );
6735
6736 view.move_right(&MoveRight, cx);
6737 assert_eq!(
6738 view.selected_display_ranges(cx),
6739 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
6740 );
6741
6742 view.move_left(&MoveLeft, cx);
6743 assert_eq!(
6744 view.selected_display_ranges(cx),
6745 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
6746 );
6747
6748 view.move_up(&MoveUp, cx);
6749 assert_eq!(
6750 view.selected_display_ranges(cx),
6751 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6752 );
6753
6754 view.move_to_end(&MoveToEnd, cx);
6755 assert_eq!(
6756 view.selected_display_ranges(cx),
6757 &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
6758 );
6759
6760 view.move_to_beginning(&MoveToBeginning, cx);
6761 assert_eq!(
6762 view.selected_display_ranges(cx),
6763 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6764 );
6765
6766 view.change_selections(None, cx, |s| {
6767 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
6768 });
6769 view.select_to_beginning(&SelectToBeginning, cx);
6770 assert_eq!(
6771 view.selected_display_ranges(cx),
6772 &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
6773 );
6774
6775 view.select_to_end(&SelectToEnd, cx);
6776 assert_eq!(
6777 view.selected_display_ranges(cx),
6778 &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
6779 );
6780 });
6781 }
6782
6783 #[gpui::test]
6784 fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
6785 cx.set_global(Settings::test(cx));
6786 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
6787 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6788
6789 assert_eq!('ⓐ'.len_utf8(), 3);
6790 assert_eq!('α'.len_utf8(), 2);
6791
6792 view.update(cx, |view, cx| {
6793 view.fold_ranges(
6794 vec![
6795 Point::new(0, 6)..Point::new(0, 12),
6796 Point::new(1, 2)..Point::new(1, 4),
6797 Point::new(2, 4)..Point::new(2, 8),
6798 ],
6799 cx,
6800 );
6801 assert_eq!(view.display_text(cx), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\n");
6802
6803 view.move_right(&MoveRight, cx);
6804 assert_eq!(
6805 view.selected_display_ranges(cx),
6806 &[empty_range(0, "ⓐ".len())]
6807 );
6808 view.move_right(&MoveRight, cx);
6809 assert_eq!(
6810 view.selected_display_ranges(cx),
6811 &[empty_range(0, "ⓐⓑ".len())]
6812 );
6813 view.move_right(&MoveRight, cx);
6814 assert_eq!(
6815 view.selected_display_ranges(cx),
6816 &[empty_range(0, "ⓐⓑ…".len())]
6817 );
6818
6819 view.move_down(&MoveDown, cx);
6820 assert_eq!(
6821 view.selected_display_ranges(cx),
6822 &[empty_range(1, "ab…".len())]
6823 );
6824 view.move_left(&MoveLeft, cx);
6825 assert_eq!(
6826 view.selected_display_ranges(cx),
6827 &[empty_range(1, "ab".len())]
6828 );
6829 view.move_left(&MoveLeft, cx);
6830 assert_eq!(
6831 view.selected_display_ranges(cx),
6832 &[empty_range(1, "a".len())]
6833 );
6834
6835 view.move_down(&MoveDown, cx);
6836 assert_eq!(
6837 view.selected_display_ranges(cx),
6838 &[empty_range(2, "α".len())]
6839 );
6840 view.move_right(&MoveRight, cx);
6841 assert_eq!(
6842 view.selected_display_ranges(cx),
6843 &[empty_range(2, "αβ".len())]
6844 );
6845 view.move_right(&MoveRight, cx);
6846 assert_eq!(
6847 view.selected_display_ranges(cx),
6848 &[empty_range(2, "αβ…".len())]
6849 );
6850 view.move_right(&MoveRight, cx);
6851 assert_eq!(
6852 view.selected_display_ranges(cx),
6853 &[empty_range(2, "αβ…ε".len())]
6854 );
6855
6856 view.move_up(&MoveUp, cx);
6857 assert_eq!(
6858 view.selected_display_ranges(cx),
6859 &[empty_range(1, "ab…e".len())]
6860 );
6861 view.move_up(&MoveUp, cx);
6862 assert_eq!(
6863 view.selected_display_ranges(cx),
6864 &[empty_range(0, "ⓐⓑ…ⓔ".len())]
6865 );
6866 view.move_left(&MoveLeft, cx);
6867 assert_eq!(
6868 view.selected_display_ranges(cx),
6869 &[empty_range(0, "ⓐⓑ…".len())]
6870 );
6871 view.move_left(&MoveLeft, cx);
6872 assert_eq!(
6873 view.selected_display_ranges(cx),
6874 &[empty_range(0, "ⓐⓑ".len())]
6875 );
6876 view.move_left(&MoveLeft, cx);
6877 assert_eq!(
6878 view.selected_display_ranges(cx),
6879 &[empty_range(0, "ⓐ".len())]
6880 );
6881 });
6882 }
6883
6884 #[gpui::test]
6885 fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
6886 cx.set_global(Settings::test(cx));
6887 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
6888 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6889 view.update(cx, |view, cx| {
6890 view.change_selections(None, cx, |s| {
6891 s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
6892 });
6893 view.move_down(&MoveDown, cx);
6894 assert_eq!(
6895 view.selected_display_ranges(cx),
6896 &[empty_range(1, "abcd".len())]
6897 );
6898
6899 view.move_down(&MoveDown, cx);
6900 assert_eq!(
6901 view.selected_display_ranges(cx),
6902 &[empty_range(2, "αβγ".len())]
6903 );
6904
6905 view.move_down(&MoveDown, cx);
6906 assert_eq!(
6907 view.selected_display_ranges(cx),
6908 &[empty_range(3, "abcd".len())]
6909 );
6910
6911 view.move_down(&MoveDown, cx);
6912 assert_eq!(
6913 view.selected_display_ranges(cx),
6914 &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
6915 );
6916
6917 view.move_up(&MoveUp, cx);
6918 assert_eq!(
6919 view.selected_display_ranges(cx),
6920 &[empty_range(3, "abcd".len())]
6921 );
6922
6923 view.move_up(&MoveUp, cx);
6924 assert_eq!(
6925 view.selected_display_ranges(cx),
6926 &[empty_range(2, "αβγ".len())]
6927 );
6928 });
6929 }
6930
6931 #[gpui::test]
6932 fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
6933 cx.set_global(Settings::test(cx));
6934 let buffer = MultiBuffer::build_simple("abc\n def", cx);
6935 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6936 view.update(cx, |view, cx| {
6937 view.change_selections(None, cx, |s| {
6938 s.select_display_ranges([
6939 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
6940 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
6941 ]);
6942 });
6943 });
6944
6945 view.update(cx, |view, cx| {
6946 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
6947 assert_eq!(
6948 view.selected_display_ranges(cx),
6949 &[
6950 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
6951 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
6952 ]
6953 );
6954 });
6955
6956 view.update(cx, |view, cx| {
6957 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
6958 assert_eq!(
6959 view.selected_display_ranges(cx),
6960 &[
6961 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
6962 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
6963 ]
6964 );
6965 });
6966
6967 view.update(cx, |view, cx| {
6968 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
6969 assert_eq!(
6970 view.selected_display_ranges(cx),
6971 &[
6972 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
6973 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
6974 ]
6975 );
6976 });
6977
6978 view.update(cx, |view, cx| {
6979 view.move_to_end_of_line(&MoveToEndOfLine, cx);
6980 assert_eq!(
6981 view.selected_display_ranges(cx),
6982 &[
6983 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
6984 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
6985 ]
6986 );
6987 });
6988
6989 // Moving to the end of line again is a no-op.
6990 view.update(cx, |view, cx| {
6991 view.move_to_end_of_line(&MoveToEndOfLine, cx);
6992 assert_eq!(
6993 view.selected_display_ranges(cx),
6994 &[
6995 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
6996 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
6997 ]
6998 );
6999 });
7000
7001 view.update(cx, |view, cx| {
7002 view.move_left(&MoveLeft, cx);
7003 view.select_to_beginning_of_line(
7004 &SelectToBeginningOfLine {
7005 stop_at_soft_wraps: true,
7006 },
7007 cx,
7008 );
7009 assert_eq!(
7010 view.selected_display_ranges(cx),
7011 &[
7012 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7013 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7014 ]
7015 );
7016 });
7017
7018 view.update(cx, |view, cx| {
7019 view.select_to_beginning_of_line(
7020 &SelectToBeginningOfLine {
7021 stop_at_soft_wraps: true,
7022 },
7023 cx,
7024 );
7025 assert_eq!(
7026 view.selected_display_ranges(cx),
7027 &[
7028 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7029 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
7030 ]
7031 );
7032 });
7033
7034 view.update(cx, |view, cx| {
7035 view.select_to_beginning_of_line(
7036 &SelectToBeginningOfLine {
7037 stop_at_soft_wraps: true,
7038 },
7039 cx,
7040 );
7041 assert_eq!(
7042 view.selected_display_ranges(cx),
7043 &[
7044 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7045 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7046 ]
7047 );
7048 });
7049
7050 view.update(cx, |view, cx| {
7051 view.select_to_end_of_line(
7052 &SelectToEndOfLine {
7053 stop_at_soft_wraps: true,
7054 },
7055 cx,
7056 );
7057 assert_eq!(
7058 view.selected_display_ranges(cx),
7059 &[
7060 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
7061 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
7062 ]
7063 );
7064 });
7065
7066 view.update(cx, |view, cx| {
7067 view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
7068 assert_eq!(view.display_text(cx), "ab\n de");
7069 assert_eq!(
7070 view.selected_display_ranges(cx),
7071 &[
7072 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7073 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7074 ]
7075 );
7076 });
7077
7078 view.update(cx, |view, cx| {
7079 view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7080 assert_eq!(view.display_text(cx), "\n");
7081 assert_eq!(
7082 view.selected_display_ranges(cx),
7083 &[
7084 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7085 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7086 ]
7087 );
7088 });
7089 }
7090
7091 #[gpui::test]
7092 fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
7093 cx.set_global(Settings::test(cx));
7094 let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
7095 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7096 view.update(cx, |view, cx| {
7097 view.change_selections(None, cx, |s| {
7098 s.select_display_ranges([
7099 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
7100 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
7101 ])
7102 });
7103
7104 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7105 assert_selection_ranges(
7106 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7107 vec![('<', '>'), ('[', ']')],
7108 view,
7109 cx,
7110 );
7111
7112 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7113 assert_selection_ranges(
7114 "use std<>::str::{foo, bar}\n\n []{baz.qux()}",
7115 vec![('<', '>'), ('[', ']')],
7116 view,
7117 cx,
7118 );
7119
7120 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7121 assert_selection_ranges(
7122 "use <>std::str::{foo, bar}\n\n[] {baz.qux()}",
7123 vec![('<', '>'), ('[', ']')],
7124 view,
7125 cx,
7126 );
7127
7128 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7129 assert_selection_ranges(
7130 "<>use std::str::{foo, bar}\n[]\n {baz.qux()}",
7131 vec![('<', '>'), ('[', ']')],
7132 view,
7133 cx,
7134 );
7135
7136 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7137 assert_selection_ranges(
7138 "<>use std::str::{foo, bar[]}\n\n {baz.qux()}",
7139 vec![('<', '>'), ('[', ']')],
7140 view,
7141 cx,
7142 );
7143
7144 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7145 assert_selection_ranges(
7146 "use<> std::str::{foo, bar}[]\n\n {baz.qux()}",
7147 vec![('<', '>'), ('[', ']')],
7148 view,
7149 cx,
7150 );
7151
7152 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7153 assert_selection_ranges(
7154 "use std<>::str::{foo, bar}\n[]\n {baz.qux()}",
7155 vec![('<', '>'), ('[', ']')],
7156 view,
7157 cx,
7158 );
7159
7160 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7161 assert_selection_ranges(
7162 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7163 vec![('<', '>'), ('[', ']')],
7164 view,
7165 cx,
7166 );
7167
7168 view.move_right(&MoveRight, cx);
7169 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7170 assert_selection_ranges(
7171 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7172 vec![('<', '>'), ('[', ']')],
7173 view,
7174 cx,
7175 );
7176
7177 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7178 assert_selection_ranges(
7179 "use std>::s<tr::{foo, bar}\n\n ]{b[az.qux()}",
7180 vec![('<', '>'), ('[', ']')],
7181 view,
7182 cx,
7183 );
7184
7185 view.select_to_next_word_end(&SelectToNextWordEnd, cx);
7186 assert_selection_ranges(
7187 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7188 vec![('<', '>'), ('[', ']')],
7189 view,
7190 cx,
7191 );
7192 });
7193 }
7194
7195 #[gpui::test]
7196 fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
7197 cx.set_global(Settings::test(cx));
7198 let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
7199 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7200
7201 view.update(cx, |view, cx| {
7202 view.set_wrap_width(Some(140.), cx);
7203 assert_eq!(
7204 view.display_text(cx),
7205 "use one::{\n two::three::\n four::five\n};"
7206 );
7207
7208 view.change_selections(None, cx, |s| {
7209 s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
7210 });
7211
7212 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7213 assert_eq!(
7214 view.selected_display_ranges(cx),
7215 &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
7216 );
7217
7218 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7219 assert_eq!(
7220 view.selected_display_ranges(cx),
7221 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7222 );
7223
7224 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7225 assert_eq!(
7226 view.selected_display_ranges(cx),
7227 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7228 );
7229
7230 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7231 assert_eq!(
7232 view.selected_display_ranges(cx),
7233 &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
7234 );
7235
7236 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7237 assert_eq!(
7238 view.selected_display_ranges(cx),
7239 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7240 );
7241
7242 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7243 assert_eq!(
7244 view.selected_display_ranges(cx),
7245 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7246 );
7247 });
7248 }
7249
7250 #[gpui::test]
7251 fn test_delete_to_beginning_of_line(cx: &mut gpui::MutableAppContext) {
7252 cx.set_global(Settings::test(cx));
7253 let (text, ranges) = marked_text_ranges("one [two three] four");
7254 let buffer = MultiBuffer::build_simple(&text, cx);
7255
7256 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7257
7258 editor.update(cx, |editor, cx| {
7259 editor.change_selections(None, cx, |s| s.select_ranges(ranges));
7260 editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7261 assert_eq!(editor.text(cx), " four");
7262 });
7263 }
7264
7265 #[gpui::test]
7266 fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
7267 cx.set_global(Settings::test(cx));
7268 let buffer = MultiBuffer::build_simple("one two three four", cx);
7269 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7270
7271 view.update(cx, |view, cx| {
7272 view.change_selections(None, cx, |s| {
7273 s.select_display_ranges([
7274 // an empty selection - the preceding word fragment is deleted
7275 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7276 // characters selected - they are deleted
7277 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
7278 ])
7279 });
7280 view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
7281 });
7282
7283 assert_eq!(buffer.read(cx).read(cx).text(), "e two te four");
7284
7285 view.update(cx, |view, cx| {
7286 view.change_selections(None, cx, |s| {
7287 s.select_display_ranges([
7288 // an empty selection - the following word fragment is deleted
7289 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7290 // characters selected - they are deleted
7291 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
7292 ])
7293 });
7294 view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
7295 });
7296
7297 assert_eq!(buffer.read(cx).read(cx).text(), "e t te our");
7298 }
7299
7300 #[gpui::test]
7301 fn test_newline(cx: &mut gpui::MutableAppContext) {
7302 cx.set_global(Settings::test(cx));
7303 let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
7304 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7305
7306 view.update(cx, |view, cx| {
7307 view.change_selections(None, cx, |s| {
7308 s.select_display_ranges([
7309 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7310 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7311 DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
7312 ])
7313 });
7314
7315 view.newline(&Newline, cx);
7316 assert_eq!(view.text(cx), "aa\naa\n \n bb\n bb\n");
7317 });
7318 }
7319
7320 #[gpui::test]
7321 fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) {
7322 cx.set_global(Settings::test(cx));
7323 let buffer = MultiBuffer::build_simple(
7324 "
7325 a
7326 b(
7327 X
7328 )
7329 c(
7330 X
7331 )
7332 "
7333 .unindent()
7334 .as_str(),
7335 cx,
7336 );
7337
7338 let (_, editor) = cx.add_window(Default::default(), |cx| {
7339 let mut editor = build_editor(buffer.clone(), cx);
7340 editor.change_selections(None, cx, |s| {
7341 s.select_ranges([
7342 Point::new(2, 4)..Point::new(2, 5),
7343 Point::new(5, 4)..Point::new(5, 5),
7344 ])
7345 });
7346 editor
7347 });
7348
7349 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7350 buffer.update(cx, |buffer, cx| {
7351 buffer.edit(
7352 [
7353 (Point::new(1, 2)..Point::new(3, 0), ""),
7354 (Point::new(4, 2)..Point::new(6, 0), ""),
7355 ],
7356 cx,
7357 );
7358 assert_eq!(
7359 buffer.read(cx).text(),
7360 "
7361 a
7362 b()
7363 c()
7364 "
7365 .unindent()
7366 );
7367 });
7368
7369 editor.update(cx, |editor, cx| {
7370 assert_eq!(
7371 editor.selected_ranges(cx),
7372 &[
7373 Point::new(1, 2)..Point::new(1, 2),
7374 Point::new(2, 2)..Point::new(2, 2),
7375 ],
7376 );
7377
7378 editor.newline(&Newline, cx);
7379 assert_eq!(
7380 editor.text(cx),
7381 "
7382 a
7383 b(
7384 )
7385 c(
7386 )
7387 "
7388 .unindent()
7389 );
7390
7391 // The selections are moved after the inserted newlines
7392 assert_eq!(
7393 editor.selected_ranges(cx),
7394 &[
7395 Point::new(2, 0)..Point::new(2, 0),
7396 Point::new(4, 0)..Point::new(4, 0),
7397 ],
7398 );
7399 });
7400 }
7401
7402 #[gpui::test]
7403 fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) {
7404 cx.set_global(Settings::test(cx));
7405 let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
7406 let (_, editor) = cx.add_window(Default::default(), |cx| {
7407 let mut editor = build_editor(buffer.clone(), cx);
7408 editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
7409 editor
7410 });
7411
7412 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7413 buffer.update(cx, |buffer, cx| {
7414 buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], cx);
7415 assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
7416 });
7417
7418 editor.update(cx, |editor, cx| {
7419 assert_eq!(editor.selected_ranges(cx), &[2..2, 7..7, 12..12],);
7420
7421 editor.insert("Z", cx);
7422 assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
7423
7424 // The selections are moved after the inserted characters
7425 assert_eq!(editor.selected_ranges(cx), &[3..3, 9..9, 15..15],);
7426 });
7427 }
7428
7429 #[gpui::test]
7430 fn test_indent_outdent(cx: &mut gpui::MutableAppContext) {
7431 cx.set_global(Settings::test(cx));
7432 let buffer = MultiBuffer::build_simple(
7433 indoc! {"
7434 one two
7435 three
7436 four"},
7437 cx,
7438 );
7439 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7440
7441 view.update(cx, |view, cx| {
7442 // two selections on the same line
7443 select_ranges(
7444 view,
7445 indoc! {"
7446 [one] [two]
7447 three
7448 four"},
7449 cx,
7450 );
7451
7452 // indent from mid-tabstop to full tabstop
7453 view.tab(&Tab, cx);
7454 assert_text_with_selections(
7455 view,
7456 indoc! {"
7457 [one] [two]
7458 three
7459 four"},
7460 cx,
7461 );
7462
7463 // outdent from 1 tabstop to 0 tabstops
7464 view.tab_prev(&TabPrev, cx);
7465 assert_text_with_selections(
7466 view,
7467 indoc! {"
7468 [one] [two]
7469 three
7470 four"},
7471 cx,
7472 );
7473
7474 // select across line ending
7475 select_ranges(
7476 view,
7477 indoc! {"
7478 one two
7479 t[hree
7480 ] four"},
7481 cx,
7482 );
7483
7484 // indent and outdent affect only the preceding line
7485 view.tab(&Tab, cx);
7486 assert_text_with_selections(
7487 view,
7488 indoc! {"
7489 one two
7490 t[hree
7491 ] four"},
7492 cx,
7493 );
7494 view.tab_prev(&TabPrev, cx);
7495 assert_text_with_selections(
7496 view,
7497 indoc! {"
7498 one two
7499 t[hree
7500 ] four"},
7501 cx,
7502 );
7503
7504 // Ensure that indenting/outdenting works when the cursor is at column 0.
7505 select_ranges(
7506 view,
7507 indoc! {"
7508 one two
7509 []three
7510 four"},
7511 cx,
7512 );
7513 view.tab(&Tab, cx);
7514 assert_text_with_selections(
7515 view,
7516 indoc! {"
7517 one two
7518 []three
7519 four"},
7520 cx,
7521 );
7522
7523 select_ranges(
7524 view,
7525 indoc! {"
7526 one two
7527 [] three
7528 four"},
7529 cx,
7530 );
7531 view.tab_prev(&TabPrev, cx);
7532 assert_text_with_selections(
7533 view,
7534 indoc! {"
7535 one two
7536 []three
7537 four"},
7538 cx,
7539 );
7540 });
7541 }
7542
7543 #[gpui::test]
7544 fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
7545 cx.set_global(
7546 Settings::test(cx)
7547 .with_overrides(
7548 "TOML",
7549 LanguageOverride {
7550 tab_size: Some(2),
7551 ..Default::default()
7552 },
7553 )
7554 .with_overrides(
7555 "Rust",
7556 LanguageOverride {
7557 tab_size: Some(4),
7558 ..Default::default()
7559 },
7560 ),
7561 );
7562 let toml_language = Arc::new(Language::new(
7563 LanguageConfig {
7564 name: "TOML".into(),
7565 ..Default::default()
7566 },
7567 None,
7568 ));
7569 let rust_language = Arc::new(Language::new(
7570 LanguageConfig {
7571 name: "Rust".into(),
7572 ..Default::default()
7573 },
7574 None,
7575 ));
7576
7577 let toml_buffer = cx
7578 .add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
7579 let rust_buffer = cx.add_model(|cx| {
7580 Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
7581 });
7582 let multibuffer = cx.add_model(|cx| {
7583 let mut multibuffer = MultiBuffer::new(0);
7584 multibuffer.push_excerpts(
7585 toml_buffer.clone(),
7586 [Point::new(0, 0)..Point::new(2, 0)],
7587 cx,
7588 );
7589 multibuffer.push_excerpts(
7590 rust_buffer.clone(),
7591 [Point::new(0, 0)..Point::new(1, 0)],
7592 cx,
7593 );
7594 multibuffer
7595 });
7596
7597 cx.add_window(Default::default(), |cx| {
7598 let mut editor = build_editor(multibuffer, cx);
7599
7600 assert_eq!(
7601 editor.text(cx),
7602 indoc! {"
7603 a = 1
7604 b = 2
7605
7606 const c: usize = 3;
7607 "}
7608 );
7609
7610 select_ranges(
7611 &mut editor,
7612 indoc! {"
7613 [a] = 1
7614 b = 2
7615
7616 [const c:] usize = 3;
7617 "},
7618 cx,
7619 );
7620
7621 editor.tab(&Tab, cx);
7622 assert_text_with_selections(
7623 &mut editor,
7624 indoc! {"
7625 [a] = 1
7626 b = 2
7627
7628 [const c:] usize = 3;
7629 "},
7630 cx,
7631 );
7632 editor.tab_prev(&TabPrev, cx);
7633 assert_text_with_selections(
7634 &mut editor,
7635 indoc! {"
7636 [a] = 1
7637 b = 2
7638
7639 [const c:] usize = 3;
7640 "},
7641 cx,
7642 );
7643
7644 editor
7645 });
7646 }
7647
7648 #[gpui::test]
7649 fn test_backspace(cx: &mut gpui::MutableAppContext) {
7650 cx.set_global(Settings::test(cx));
7651 let (_, view) = cx.add_window(Default::default(), |cx| {
7652 build_editor(MultiBuffer::build_simple("", cx), cx)
7653 });
7654
7655 view.update(cx, |view, cx| {
7656 view.set_text("one two three\nfour five six\nseven eight nine\nten\n", cx);
7657 view.change_selections(None, cx, |s| {
7658 s.select_display_ranges([
7659 // an empty selection - the preceding character is deleted
7660 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7661 // one character selected - it is deleted
7662 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
7663 // a line suffix selected - it is deleted
7664 DisplayPoint::new(2, 6)..DisplayPoint::new(3, 0),
7665 ])
7666 });
7667 view.backspace(&Backspace, cx);
7668 assert_eq!(view.text(cx), "oe two three\nfou five six\nseven ten\n");
7669
7670 view.set_text(" one\n two\n three\n four", cx);
7671 view.change_selections(None, cx, |s| {
7672 s.select_display_ranges([
7673 // cursors at the the end of leading indent - last indent is deleted
7674 DisplayPoint::new(0, 4)..DisplayPoint::new(0, 4),
7675 DisplayPoint::new(1, 8)..DisplayPoint::new(1, 8),
7676 // cursors inside leading indent - overlapping indent deletions are coalesced
7677 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
7678 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
7679 DisplayPoint::new(2, 6)..DisplayPoint::new(2, 6),
7680 // cursor at the beginning of a line - preceding newline is deleted
7681 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7682 // selection inside leading indent - only the selected character is deleted
7683 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 3),
7684 ])
7685 });
7686 view.backspace(&Backspace, cx);
7687 assert_eq!(view.text(cx), "one\n two\n three four");
7688 });
7689 }
7690
7691 #[gpui::test]
7692 fn test_delete(cx: &mut gpui::MutableAppContext) {
7693 cx.set_global(Settings::test(cx));
7694 let buffer =
7695 MultiBuffer::build_simple("one two three\nfour five six\nseven eight nine\nten\n", cx);
7696 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7697
7698 view.update(cx, |view, cx| {
7699 view.change_selections(None, cx, |s| {
7700 s.select_display_ranges([
7701 // an empty selection - the following character is deleted
7702 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7703 // one character selected - it is deleted
7704 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
7705 // a line suffix selected - it is deleted
7706 DisplayPoint::new(2, 6)..DisplayPoint::new(3, 0),
7707 ])
7708 });
7709 view.delete(&Delete, cx);
7710 });
7711
7712 assert_eq!(
7713 buffer.read(cx).read(cx).text(),
7714 "on two three\nfou five six\nseven ten\n"
7715 );
7716 }
7717
7718 #[gpui::test]
7719 fn test_delete_line(cx: &mut gpui::MutableAppContext) {
7720 cx.set_global(Settings::test(cx));
7721 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7722 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7723 view.update(cx, |view, cx| {
7724 view.change_selections(None, cx, |s| {
7725 s.select_display_ranges([
7726 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7727 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
7728 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7729 ])
7730 });
7731 view.delete_line(&DeleteLine, cx);
7732 assert_eq!(view.display_text(cx), "ghi");
7733 assert_eq!(
7734 view.selected_display_ranges(cx),
7735 vec![
7736 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7737 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
7738 ]
7739 );
7740 });
7741
7742 cx.set_global(Settings::test(cx));
7743 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7744 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7745 view.update(cx, |view, cx| {
7746 view.change_selections(None, cx, |s| {
7747 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
7748 });
7749 view.delete_line(&DeleteLine, cx);
7750 assert_eq!(view.display_text(cx), "ghi\n");
7751 assert_eq!(
7752 view.selected_display_ranges(cx),
7753 vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
7754 );
7755 });
7756 }
7757
7758 #[gpui::test]
7759 fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
7760 cx.set_global(Settings::test(cx));
7761 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7762 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7763 view.update(cx, |view, cx| {
7764 view.change_selections(None, cx, |s| {
7765 s.select_display_ranges([
7766 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
7767 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7768 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7769 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7770 ])
7771 });
7772 view.duplicate_line(&DuplicateLine, cx);
7773 assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
7774 assert_eq!(
7775 view.selected_display_ranges(cx),
7776 vec![
7777 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
7778 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7779 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7780 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
7781 ]
7782 );
7783 });
7784
7785 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7786 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7787 view.update(cx, |view, cx| {
7788 view.change_selections(None, cx, |s| {
7789 s.select_display_ranges([
7790 DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
7791 DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
7792 ])
7793 });
7794 view.duplicate_line(&DuplicateLine, cx);
7795 assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
7796 assert_eq!(
7797 view.selected_display_ranges(cx),
7798 vec![
7799 DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
7800 DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
7801 ]
7802 );
7803 });
7804 }
7805
7806 #[gpui::test]
7807 fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
7808 cx.set_global(Settings::test(cx));
7809 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
7810 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7811 view.update(cx, |view, cx| {
7812 view.fold_ranges(
7813 vec![
7814 Point::new(0, 2)..Point::new(1, 2),
7815 Point::new(2, 3)..Point::new(4, 1),
7816 Point::new(7, 0)..Point::new(8, 4),
7817 ],
7818 cx,
7819 );
7820 view.change_selections(None, cx, |s| {
7821 s.select_display_ranges([
7822 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7823 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
7824 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
7825 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
7826 ])
7827 });
7828 assert_eq!(
7829 view.display_text(cx),
7830 "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
7831 );
7832
7833 view.move_line_up(&MoveLineUp, cx);
7834 assert_eq!(
7835 view.display_text(cx),
7836 "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
7837 );
7838 assert_eq!(
7839 view.selected_display_ranges(cx),
7840 vec![
7841 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7842 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
7843 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
7844 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
7845 ]
7846 );
7847 });
7848
7849 view.update(cx, |view, cx| {
7850 view.move_line_down(&MoveLineDown, cx);
7851 assert_eq!(
7852 view.display_text(cx),
7853 "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
7854 );
7855 assert_eq!(
7856 view.selected_display_ranges(cx),
7857 vec![
7858 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
7859 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
7860 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
7861 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
7862 ]
7863 );
7864 });
7865
7866 view.update(cx, |view, cx| {
7867 view.move_line_down(&MoveLineDown, cx);
7868 assert_eq!(
7869 view.display_text(cx),
7870 "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
7871 );
7872 assert_eq!(
7873 view.selected_display_ranges(cx),
7874 vec![
7875 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
7876 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
7877 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
7878 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
7879 ]
7880 );
7881 });
7882
7883 view.update(cx, |view, cx| {
7884 view.move_line_up(&MoveLineUp, cx);
7885 assert_eq!(
7886 view.display_text(cx),
7887 "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
7888 );
7889 assert_eq!(
7890 view.selected_display_ranges(cx),
7891 vec![
7892 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
7893 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
7894 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
7895 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
7896 ]
7897 );
7898 });
7899 }
7900
7901 #[gpui::test]
7902 fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
7903 cx.set_global(Settings::test(cx));
7904 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
7905 let snapshot = buffer.read(cx).snapshot(cx);
7906 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7907 editor.update(cx, |editor, cx| {
7908 editor.insert_blocks(
7909 [BlockProperties {
7910 position: snapshot.anchor_after(Point::new(2, 0)),
7911 disposition: BlockDisposition::Below,
7912 height: 1,
7913 render: Arc::new(|_| Empty::new().boxed()),
7914 }],
7915 cx,
7916 );
7917 editor.change_selections(None, cx, |s| {
7918 s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
7919 });
7920 editor.move_line_down(&MoveLineDown, cx);
7921 });
7922 }
7923
7924 #[gpui::test]
7925 fn test_transpose(cx: &mut gpui::MutableAppContext) {
7926 cx.set_global(Settings::test(cx));
7927
7928 cx.add_window(Default::default(), |cx| {
7929 let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
7930
7931 editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
7932 editor.transpose(&Default::default(), cx);
7933 assert_eq!(editor.text(cx), "bac");
7934 assert_eq!(editor.selected_ranges(cx), [2..2]);
7935
7936 editor.transpose(&Default::default(), cx);
7937 assert_eq!(editor.text(cx), "bca");
7938 assert_eq!(editor.selected_ranges(cx), [3..3]);
7939
7940 editor.transpose(&Default::default(), cx);
7941 assert_eq!(editor.text(cx), "bac");
7942 assert_eq!(editor.selected_ranges(cx), [3..3]);
7943
7944 editor
7945 })
7946 .1;
7947
7948 cx.add_window(Default::default(), |cx| {
7949 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
7950
7951 editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
7952 editor.transpose(&Default::default(), cx);
7953 assert_eq!(editor.text(cx), "acb\nde");
7954 assert_eq!(editor.selected_ranges(cx), [3..3]);
7955
7956 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
7957 editor.transpose(&Default::default(), cx);
7958 assert_eq!(editor.text(cx), "acbd\ne");
7959 assert_eq!(editor.selected_ranges(cx), [5..5]);
7960
7961 editor.transpose(&Default::default(), cx);
7962 assert_eq!(editor.text(cx), "acbde\n");
7963 assert_eq!(editor.selected_ranges(cx), [6..6]);
7964
7965 editor.transpose(&Default::default(), cx);
7966 assert_eq!(editor.text(cx), "acbd\ne");
7967 assert_eq!(editor.selected_ranges(cx), [6..6]);
7968
7969 editor
7970 })
7971 .1;
7972
7973 cx.add_window(Default::default(), |cx| {
7974 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
7975
7976 editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
7977 editor.transpose(&Default::default(), cx);
7978 assert_eq!(editor.text(cx), "bacd\ne");
7979 assert_eq!(editor.selected_ranges(cx), [2..2, 3..3, 5..5]);
7980
7981 editor.transpose(&Default::default(), cx);
7982 assert_eq!(editor.text(cx), "bcade\n");
7983 assert_eq!(editor.selected_ranges(cx), [3..3, 4..4, 6..6]);
7984
7985 editor.transpose(&Default::default(), cx);
7986 assert_eq!(editor.text(cx), "bcda\ne");
7987 assert_eq!(editor.selected_ranges(cx), [4..4, 6..6]);
7988
7989 editor.transpose(&Default::default(), cx);
7990 assert_eq!(editor.text(cx), "bcade\n");
7991 assert_eq!(editor.selected_ranges(cx), [4..4, 6..6]);
7992
7993 editor.transpose(&Default::default(), cx);
7994 assert_eq!(editor.text(cx), "bcaed\n");
7995 assert_eq!(editor.selected_ranges(cx), [5..5, 6..6]);
7996
7997 editor
7998 })
7999 .1;
8000
8001 cx.add_window(Default::default(), |cx| {
8002 let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
8003
8004 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8005 editor.transpose(&Default::default(), cx);
8006 assert_eq!(editor.text(cx), "🏀🍐✋");
8007 assert_eq!(editor.selected_ranges(cx), [8..8]);
8008
8009 editor.transpose(&Default::default(), cx);
8010 assert_eq!(editor.text(cx), "🏀✋🍐");
8011 assert_eq!(editor.selected_ranges(cx), [11..11]);
8012
8013 editor.transpose(&Default::default(), cx);
8014 assert_eq!(editor.text(cx), "🏀🍐✋");
8015 assert_eq!(editor.selected_ranges(cx), [11..11]);
8016
8017 editor
8018 })
8019 .1;
8020 }
8021
8022 #[gpui::test]
8023 fn test_clipboard(cx: &mut gpui::MutableAppContext) {
8024 cx.set_global(Settings::test(cx));
8025 let buffer = MultiBuffer::build_simple("one✅ two three four five six ", cx);
8026 let view = cx
8027 .add_window(Default::default(), |cx| build_editor(buffer.clone(), cx))
8028 .1;
8029
8030 // Cut with three selections. Clipboard text is divided into three slices.
8031 view.update(cx, |view, cx| {
8032 view.change_selections(None, cx, |s| s.select_ranges(vec![0..7, 11..17, 22..27]));
8033 view.cut(&Cut, cx);
8034 assert_eq!(view.display_text(cx), "two four six ");
8035 });
8036
8037 // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
8038 view.update(cx, |view, cx| {
8039 view.change_selections(None, cx, |s| s.select_ranges(vec![4..4, 9..9, 13..13]));
8040 view.paste(&Paste, cx);
8041 assert_eq!(view.display_text(cx), "two one✅ four three six five ");
8042 assert_eq!(
8043 view.selected_display_ranges(cx),
8044 &[
8045 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
8046 DisplayPoint::new(0, 22)..DisplayPoint::new(0, 22),
8047 DisplayPoint::new(0, 31)..DisplayPoint::new(0, 31)
8048 ]
8049 );
8050 });
8051
8052 // Paste again but with only two cursors. Since the number of cursors doesn't
8053 // match the number of slices in the clipboard, the entire clipboard text
8054 // is pasted at each cursor.
8055 view.update(cx, |view, cx| {
8056 view.change_selections(None, cx, |s| s.select_ranges(vec![0..0, 31..31]));
8057 view.handle_input(&Input("( ".into()), cx);
8058 view.paste(&Paste, cx);
8059 view.handle_input(&Input(") ".into()), cx);
8060 assert_eq!(
8061 view.display_text(cx),
8062 "( one✅ \nthree \nfive ) two one✅ four three six five ( one✅ \nthree \nfive ) "
8063 );
8064 });
8065
8066 view.update(cx, |view, cx| {
8067 view.change_selections(None, cx, |s| s.select_ranges(vec![0..0]));
8068 view.handle_input(&Input("123\n4567\n89\n".into()), cx);
8069 assert_eq!(
8070 view.display_text(cx),
8071 "123\n4567\n89\n( one✅ \nthree \nfive ) two one✅ four three six five ( one✅ \nthree \nfive ) "
8072 );
8073 });
8074
8075 // Cut with three selections, one of which is full-line.
8076 view.update(cx, |view, cx| {
8077 view.change_selections(None, cx, |s| s.select_display_ranges(
8078 [
8079 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2),
8080 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8081 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
8082 ],
8083 ));
8084 view.cut(&Cut, cx);
8085 assert_eq!(
8086 view.display_text(cx),
8087 "13\n9\n( one✅ \nthree \nfive ) two one✅ four three six five ( one✅ \nthree \nfive ) "
8088 );
8089 });
8090
8091 // Paste with three selections, noticing how the copied selection that was full-line
8092 // gets inserted before the second cursor.
8093 view.update(cx, |view, cx| {
8094 view.change_selections(None, cx, |s| s.select_display_ranges(
8095 [
8096 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8097 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8098 DisplayPoint::new(2, 2)..DisplayPoint::new(2, 3),
8099 ],
8100 ));
8101 view.paste(&Paste, cx);
8102 assert_eq!(
8103 view.display_text(cx),
8104 "123\n4567\n9\n( 8ne✅ \nthree \nfive ) two one✅ four three six five ( one✅ \nthree \nfive ) "
8105 );
8106 assert_eq!(
8107 view.selected_display_ranges(cx),
8108 &[
8109 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8110 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8111 DisplayPoint::new(3, 3)..DisplayPoint::new(3, 3),
8112 ]
8113 );
8114 });
8115
8116 // Copy with a single cursor only, which writes the whole line into the clipboard.
8117 view.update(cx, |view, cx| {
8118 view.change_selections(None, cx, |s| {
8119 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)])
8120 });
8121 view.copy(&Copy, cx);
8122 });
8123
8124 // Paste with three selections, noticing how the copied full-line selection is inserted
8125 // before the empty selections but replaces the selection that is non-empty.
8126 view.update(cx, |view, cx| {
8127 view.change_selections(None, cx, |s| s.select_display_ranges(
8128 [
8129 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8130 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 2),
8131 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8132 ],
8133 ));
8134 view.paste(&Paste, cx);
8135 assert_eq!(
8136 view.display_text(cx),
8137 "123\n123\n123\n67\n123\n9\n( 8ne✅ \nthree \nfive ) two one✅ four three six five ( one✅ \nthree \nfive ) "
8138 );
8139 assert_eq!(
8140 view.selected_display_ranges(cx),
8141 &[
8142 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8143 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8144 DisplayPoint::new(5, 1)..DisplayPoint::new(5, 1),
8145 ]
8146 );
8147 });
8148 }
8149
8150 #[gpui::test]
8151 fn test_select_all(cx: &mut gpui::MutableAppContext) {
8152 cx.set_global(Settings::test(cx));
8153 let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
8154 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8155 view.update(cx, |view, cx| {
8156 view.select_all(&SelectAll, cx);
8157 assert_eq!(
8158 view.selected_display_ranges(cx),
8159 &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
8160 );
8161 });
8162 }
8163
8164 #[gpui::test]
8165 fn test_select_line(cx: &mut gpui::MutableAppContext) {
8166 cx.set_global(Settings::test(cx));
8167 let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
8168 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8169 view.update(cx, |view, cx| {
8170 view.change_selections(None, cx, |s| {
8171 s.select_display_ranges([
8172 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8173 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8174 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8175 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
8176 ])
8177 });
8178 view.select_line(&SelectLine, cx);
8179 assert_eq!(
8180 view.selected_display_ranges(cx),
8181 vec![
8182 DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
8183 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
8184 ]
8185 );
8186 });
8187
8188 view.update(cx, |view, cx| {
8189 view.select_line(&SelectLine, cx);
8190 assert_eq!(
8191 view.selected_display_ranges(cx),
8192 vec![
8193 DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
8194 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
8195 ]
8196 );
8197 });
8198
8199 view.update(cx, |view, cx| {
8200 view.select_line(&SelectLine, cx);
8201 assert_eq!(
8202 view.selected_display_ranges(cx),
8203 vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
8204 );
8205 });
8206 }
8207
8208 #[gpui::test]
8209 fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
8210 cx.set_global(Settings::test(cx));
8211 let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
8212 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8213 view.update(cx, |view, cx| {
8214 view.fold_ranges(
8215 vec![
8216 Point::new(0, 2)..Point::new(1, 2),
8217 Point::new(2, 3)..Point::new(4, 1),
8218 Point::new(7, 0)..Point::new(8, 4),
8219 ],
8220 cx,
8221 );
8222 view.change_selections(None, cx, |s| {
8223 s.select_display_ranges([
8224 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8225 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8226 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8227 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
8228 ])
8229 });
8230 assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i");
8231 });
8232
8233 view.update(cx, |view, cx| {
8234 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8235 assert_eq!(
8236 view.display_text(cx),
8237 "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i"
8238 );
8239 assert_eq!(
8240 view.selected_display_ranges(cx),
8241 [
8242 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8243 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8244 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
8245 DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
8246 ]
8247 );
8248 });
8249
8250 view.update(cx, |view, cx| {
8251 view.change_selections(None, cx, |s| {
8252 s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
8253 });
8254 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8255 assert_eq!(
8256 view.display_text(cx),
8257 "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
8258 );
8259 assert_eq!(
8260 view.selected_display_ranges(cx),
8261 [
8262 DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
8263 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
8264 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
8265 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
8266 DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
8267 DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
8268 DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
8269 DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
8270 ]
8271 );
8272 });
8273 }
8274
8275 #[gpui::test]
8276 fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
8277 cx.set_global(Settings::test(cx));
8278 let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
8279 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8280
8281 view.update(cx, |view, cx| {
8282 view.change_selections(None, cx, |s| {
8283 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
8284 });
8285 });
8286 view.update(cx, |view, cx| {
8287 view.add_selection_above(&AddSelectionAbove, cx);
8288 assert_eq!(
8289 view.selected_display_ranges(cx),
8290 vec![
8291 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8292 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8293 ]
8294 );
8295 });
8296
8297 view.update(cx, |view, cx| {
8298 view.add_selection_above(&AddSelectionAbove, cx);
8299 assert_eq!(
8300 view.selected_display_ranges(cx),
8301 vec![
8302 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8303 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8304 ]
8305 );
8306 });
8307
8308 view.update(cx, |view, cx| {
8309 view.add_selection_below(&AddSelectionBelow, cx);
8310 assert_eq!(
8311 view.selected_display_ranges(cx),
8312 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8313 );
8314
8315 view.undo_selection(&UndoSelection, cx);
8316 assert_eq!(
8317 view.selected_display_ranges(cx),
8318 vec![
8319 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8320 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8321 ]
8322 );
8323
8324 view.redo_selection(&RedoSelection, cx);
8325 assert_eq!(
8326 view.selected_display_ranges(cx),
8327 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8328 );
8329 });
8330
8331 view.update(cx, |view, cx| {
8332 view.add_selection_below(&AddSelectionBelow, cx);
8333 assert_eq!(
8334 view.selected_display_ranges(cx),
8335 vec![
8336 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8337 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8338 ]
8339 );
8340 });
8341
8342 view.update(cx, |view, cx| {
8343 view.add_selection_below(&AddSelectionBelow, cx);
8344 assert_eq!(
8345 view.selected_display_ranges(cx),
8346 vec![
8347 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8348 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8349 ]
8350 );
8351 });
8352
8353 view.update(cx, |view, cx| {
8354 view.change_selections(None, cx, |s| {
8355 s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
8356 });
8357 });
8358 view.update(cx, |view, cx| {
8359 view.add_selection_below(&AddSelectionBelow, cx);
8360 assert_eq!(
8361 view.selected_display_ranges(cx),
8362 vec![
8363 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8364 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8365 ]
8366 );
8367 });
8368
8369 view.update(cx, |view, cx| {
8370 view.add_selection_below(&AddSelectionBelow, cx);
8371 assert_eq!(
8372 view.selected_display_ranges(cx),
8373 vec![
8374 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8375 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8376 ]
8377 );
8378 });
8379
8380 view.update(cx, |view, cx| {
8381 view.add_selection_above(&AddSelectionAbove, cx);
8382 assert_eq!(
8383 view.selected_display_ranges(cx),
8384 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
8385 );
8386 });
8387
8388 view.update(cx, |view, cx| {
8389 view.add_selection_above(&AddSelectionAbove, cx);
8390 assert_eq!(
8391 view.selected_display_ranges(cx),
8392 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
8393 );
8394 });
8395
8396 view.update(cx, |view, cx| {
8397 view.change_selections(None, cx, |s| {
8398 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
8399 });
8400 view.add_selection_below(&AddSelectionBelow, cx);
8401 assert_eq!(
8402 view.selected_display_ranges(cx),
8403 vec![
8404 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8405 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8406 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8407 ]
8408 );
8409 });
8410
8411 view.update(cx, |view, cx| {
8412 view.add_selection_below(&AddSelectionBelow, cx);
8413 assert_eq!(
8414 view.selected_display_ranges(cx),
8415 vec![
8416 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8417 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8418 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8419 DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
8420 ]
8421 );
8422 });
8423
8424 view.update(cx, |view, cx| {
8425 view.add_selection_above(&AddSelectionAbove, cx);
8426 assert_eq!(
8427 view.selected_display_ranges(cx),
8428 vec![
8429 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8430 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8431 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8432 ]
8433 );
8434 });
8435
8436 view.update(cx, |view, cx| {
8437 view.change_selections(None, cx, |s| {
8438 s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
8439 });
8440 });
8441 view.update(cx, |view, cx| {
8442 view.add_selection_above(&AddSelectionAbove, cx);
8443 assert_eq!(
8444 view.selected_display_ranges(cx),
8445 vec![
8446 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
8447 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
8448 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
8449 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
8450 ]
8451 );
8452 });
8453
8454 view.update(cx, |view, cx| {
8455 view.add_selection_below(&AddSelectionBelow, cx);
8456 assert_eq!(
8457 view.selected_display_ranges(cx),
8458 vec![
8459 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
8460 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
8461 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
8462 ]
8463 );
8464 });
8465 }
8466
8467 #[gpui::test]
8468 fn test_select_next(cx: &mut gpui::MutableAppContext) {
8469 cx.set_global(Settings::test(cx));
8470
8471 let (text, ranges) = marked_text_ranges("[abc]\n[abc] [abc]\ndefabc\n[abc]");
8472 let buffer = MultiBuffer::build_simple(&text, cx);
8473 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8474
8475 view.update(cx, |view, cx| {
8476 view.change_selections(None, cx, |s| {
8477 s.select_ranges([ranges[1].start + 1..ranges[1].start + 1])
8478 });
8479 view.select_next(
8480 &SelectNext {
8481 replace_newest: false,
8482 },
8483 cx,
8484 );
8485 assert_eq!(view.selected_ranges(cx), &ranges[1..2]);
8486
8487 view.select_next(
8488 &SelectNext {
8489 replace_newest: false,
8490 },
8491 cx,
8492 );
8493 assert_eq!(view.selected_ranges(cx), &ranges[1..3]);
8494
8495 view.undo_selection(&UndoSelection, cx);
8496 assert_eq!(view.selected_ranges(cx), &ranges[1..2]);
8497
8498 view.redo_selection(&RedoSelection, cx);
8499 assert_eq!(view.selected_ranges(cx), &ranges[1..3]);
8500
8501 view.select_next(
8502 &SelectNext {
8503 replace_newest: false,
8504 },
8505 cx,
8506 );
8507 assert_eq!(view.selected_ranges(cx), &ranges[1..4]);
8508
8509 view.select_next(
8510 &SelectNext {
8511 replace_newest: false,
8512 },
8513 cx,
8514 );
8515 assert_eq!(view.selected_ranges(cx), &ranges[0..4]);
8516 });
8517 }
8518
8519 #[gpui::test]
8520 async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
8521 cx.update(|cx| cx.set_global(Settings::test(cx)));
8522 let language = Arc::new(Language::new(
8523 LanguageConfig::default(),
8524 Some(tree_sitter_rust::language()),
8525 ));
8526
8527 let text = r#"
8528 use mod1::mod2::{mod3, mod4};
8529
8530 fn fn_1(param1: bool, param2: &str) {
8531 let var1 = "text";
8532 }
8533 "#
8534 .unindent();
8535
8536 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8537 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8538 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8539 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8540 .await;
8541
8542 view.update(cx, |view, cx| {
8543 view.change_selections(None, cx, |s| {
8544 s.select_display_ranges([
8545 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8546 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8547 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8548 ]);
8549 });
8550 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8551 });
8552 assert_eq!(
8553 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8554 &[
8555 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8556 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8557 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
8558 ]
8559 );
8560
8561 view.update(cx, |view, cx| {
8562 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8563 });
8564 assert_eq!(
8565 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8566 &[
8567 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8568 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8569 ]
8570 );
8571
8572 view.update(cx, |view, cx| {
8573 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8574 });
8575 assert_eq!(
8576 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8577 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8578 );
8579
8580 // Trying to expand the selected syntax node one more time has no effect.
8581 view.update(cx, |view, cx| {
8582 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8583 });
8584 assert_eq!(
8585 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8586 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8587 );
8588
8589 view.update(cx, |view, cx| {
8590 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8591 });
8592 assert_eq!(
8593 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8594 &[
8595 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8596 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8597 ]
8598 );
8599
8600 view.update(cx, |view, cx| {
8601 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8602 });
8603 assert_eq!(
8604 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8605 &[
8606 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8607 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8608 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
8609 ]
8610 );
8611
8612 view.update(cx, |view, cx| {
8613 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8614 });
8615 assert_eq!(
8616 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8617 &[
8618 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8619 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8620 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8621 ]
8622 );
8623
8624 // Trying to shrink the selected syntax node one more time has no effect.
8625 view.update(cx, |view, cx| {
8626 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8627 });
8628 assert_eq!(
8629 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8630 &[
8631 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8632 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8633 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8634 ]
8635 );
8636
8637 // Ensure that we keep expanding the selection if the larger selection starts or ends within
8638 // a fold.
8639 view.update(cx, |view, cx| {
8640 view.fold_ranges(
8641 vec![
8642 Point::new(0, 21)..Point::new(0, 24),
8643 Point::new(3, 20)..Point::new(3, 22),
8644 ],
8645 cx,
8646 );
8647 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8648 });
8649 assert_eq!(
8650 view.update(cx, |view, cx| view.selected_display_ranges(cx)),
8651 &[
8652 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8653 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8654 DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
8655 ]
8656 );
8657 }
8658
8659 #[gpui::test]
8660 async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
8661 cx.update(|cx| cx.set_global(Settings::test(cx)));
8662 let language = Arc::new(
8663 Language::new(
8664 LanguageConfig {
8665 brackets: vec![
8666 BracketPair {
8667 start: "{".to_string(),
8668 end: "}".to_string(),
8669 close: false,
8670 newline: true,
8671 },
8672 BracketPair {
8673 start: "(".to_string(),
8674 end: ")".to_string(),
8675 close: false,
8676 newline: true,
8677 },
8678 ],
8679 ..Default::default()
8680 },
8681 Some(tree_sitter_rust::language()),
8682 )
8683 .with_indents_query(
8684 r#"
8685 (_ "(" ")" @end) @indent
8686 (_ "{" "}" @end) @indent
8687 "#,
8688 )
8689 .unwrap(),
8690 );
8691
8692 let text = "fn a() {}";
8693
8694 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8695 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8696 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
8697 editor
8698 .condition(&cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
8699 .await;
8700
8701 editor.update(cx, |editor, cx| {
8702 editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
8703 editor.newline(&Newline, cx);
8704 assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
8705 assert_eq!(
8706 editor.selected_ranges(cx),
8707 &[
8708 Point::new(1, 4)..Point::new(1, 4),
8709 Point::new(3, 4)..Point::new(3, 4),
8710 Point::new(5, 0)..Point::new(5, 0)
8711 ]
8712 );
8713 });
8714 }
8715
8716 #[gpui::test]
8717 async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
8718 cx.update(|cx| cx.set_global(Settings::test(cx)));
8719 let language = Arc::new(Language::new(
8720 LanguageConfig {
8721 brackets: vec![
8722 BracketPair {
8723 start: "{".to_string(),
8724 end: "}".to_string(),
8725 close: true,
8726 newline: true,
8727 },
8728 BracketPair {
8729 start: "/*".to_string(),
8730 end: " */".to_string(),
8731 close: true,
8732 newline: true,
8733 },
8734 ],
8735 autoclose_before: "})]".to_string(),
8736 ..Default::default()
8737 },
8738 Some(tree_sitter_rust::language()),
8739 ));
8740
8741 let text = r#"
8742 a
8743
8744 /
8745
8746 "#
8747 .unindent();
8748
8749 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8750 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8751 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8752 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8753 .await;
8754
8755 view.update(cx, |view, cx| {
8756 view.change_selections(None, cx, |s| {
8757 s.select_display_ranges([
8758 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8759 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8760 ])
8761 });
8762
8763 view.handle_input(&Input("{".to_string()), cx);
8764 view.handle_input(&Input("{".to_string()), cx);
8765 view.handle_input(&Input("{".to_string()), cx);
8766 assert_eq!(
8767 view.text(cx),
8768 "
8769 {{{}}}
8770 {{{}}}
8771 /
8772
8773 "
8774 .unindent()
8775 );
8776
8777 view.move_right(&MoveRight, cx);
8778 view.handle_input(&Input("}".to_string()), cx);
8779 view.handle_input(&Input("}".to_string()), cx);
8780 view.handle_input(&Input("}".to_string()), cx);
8781 assert_eq!(
8782 view.text(cx),
8783 "
8784 {{{}}}}
8785 {{{}}}}
8786 /
8787
8788 "
8789 .unindent()
8790 );
8791
8792 view.undo(&Undo, cx);
8793 view.handle_input(&Input("/".to_string()), cx);
8794 view.handle_input(&Input("*".to_string()), cx);
8795 assert_eq!(
8796 view.text(cx),
8797 "
8798 /* */
8799 /* */
8800 /
8801
8802 "
8803 .unindent()
8804 );
8805
8806 view.undo(&Undo, cx);
8807 view.change_selections(None, cx, |s| {
8808 s.select_display_ranges([
8809 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8810 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8811 ])
8812 });
8813 view.handle_input(&Input("*".to_string()), cx);
8814 assert_eq!(
8815 view.text(cx),
8816 "
8817 a
8818
8819 /*
8820 *
8821 "
8822 .unindent()
8823 );
8824
8825 // Don't autoclose if the next character isn't whitespace and isn't
8826 // listed in the language's "autoclose_before" section.
8827 view.finalize_last_transaction(cx);
8828 view.change_selections(None, cx, |s| {
8829 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)])
8830 });
8831 view.handle_input(&Input("{".to_string()), cx);
8832 assert_eq!(
8833 view.text(cx),
8834 "
8835 {a
8836
8837 /*
8838 *
8839 "
8840 .unindent()
8841 );
8842
8843 view.undo(&Undo, cx);
8844 view.change_selections(None, cx, |s| {
8845 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1)])
8846 });
8847 view.handle_input(&Input("{".to_string()), cx);
8848 assert_eq!(
8849 view.text(cx),
8850 "
8851 {a}
8852
8853 /*
8854 *
8855 "
8856 .unindent()
8857 );
8858 assert_eq!(
8859 view.selected_display_ranges(cx),
8860 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
8861 );
8862 });
8863 }
8864
8865 #[gpui::test]
8866 async fn test_snippets(cx: &mut gpui::TestAppContext) {
8867 cx.update(|cx| cx.set_global(Settings::test(cx)));
8868
8869 let (text, insertion_ranges) = marked_text_ranges(indoc! {"
8870 a.| b
8871 a.| b
8872 a.| b"});
8873 let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
8874 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
8875
8876 editor.update(cx, |editor, cx| {
8877 let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
8878
8879 editor
8880 .insert_snippet(&insertion_ranges, snippet, cx)
8881 .unwrap();
8882
8883 fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text_ranges: &str) {
8884 let range_markers = ('<', '>');
8885 let (expected_text, mut selection_ranges_lookup) =
8886 marked_text_ranges_by(marked_text_ranges, vec![range_markers.clone()]);
8887 let selection_ranges = selection_ranges_lookup.remove(&range_markers).unwrap();
8888 assert_eq!(editor.text(cx), expected_text);
8889 assert_eq!(editor.selected_ranges::<usize>(cx), selection_ranges);
8890 }
8891 assert(
8892 editor,
8893 cx,
8894 indoc! {"
8895 a.f(<one>, two, <three>) b
8896 a.f(<one>, two, <three>) b
8897 a.f(<one>, two, <three>) b"},
8898 );
8899
8900 // Can't move earlier than the first tab stop
8901 assert!(!editor.move_to_prev_snippet_tabstop(cx));
8902 assert(
8903 editor,
8904 cx,
8905 indoc! {"
8906 a.f(<one>, two, <three>) b
8907 a.f(<one>, two, <three>) b
8908 a.f(<one>, two, <three>) b"},
8909 );
8910
8911 assert!(editor.move_to_next_snippet_tabstop(cx));
8912 assert(
8913 editor,
8914 cx,
8915 indoc! {"
8916 a.f(one, <two>, three) b
8917 a.f(one, <two>, three) b
8918 a.f(one, <two>, three) b"},
8919 );
8920
8921 editor.move_to_prev_snippet_tabstop(cx);
8922 assert(
8923 editor,
8924 cx,
8925 indoc! {"
8926 a.f(<one>, two, <three>) b
8927 a.f(<one>, two, <three>) b
8928 a.f(<one>, two, <three>) b"},
8929 );
8930
8931 assert!(editor.move_to_next_snippet_tabstop(cx));
8932 assert(
8933 editor,
8934 cx,
8935 indoc! {"
8936 a.f(one, <two>, three) b
8937 a.f(one, <two>, three) b
8938 a.f(one, <two>, three) b"},
8939 );
8940 assert!(editor.move_to_next_snippet_tabstop(cx));
8941 assert(
8942 editor,
8943 cx,
8944 indoc! {"
8945 a.f(one, two, three)<> b
8946 a.f(one, two, three)<> b
8947 a.f(one, two, three)<> b"},
8948 );
8949
8950 // As soon as the last tab stop is reached, snippet state is gone
8951 editor.move_to_prev_snippet_tabstop(cx);
8952 assert(
8953 editor,
8954 cx,
8955 indoc! {"
8956 a.f(one, two, three)<> b
8957 a.f(one, two, three)<> b
8958 a.f(one, two, three)<> b"},
8959 );
8960 });
8961 }
8962
8963 #[gpui::test]
8964 async fn test_format_during_save(cx: &mut gpui::TestAppContext) {
8965 cx.foreground().forbid_parking();
8966 cx.update(|cx| cx.set_global(Settings::test(cx)));
8967
8968 let mut language = Language::new(
8969 LanguageConfig {
8970 name: "Rust".into(),
8971 path_suffixes: vec!["rs".to_string()],
8972 ..Default::default()
8973 },
8974 Some(tree_sitter_rust::language()),
8975 );
8976 let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
8977 capabilities: lsp::ServerCapabilities {
8978 document_formatting_provider: Some(lsp::OneOf::Left(true)),
8979 ..Default::default()
8980 },
8981 ..Default::default()
8982 });
8983
8984 let fs = FakeFs::new(cx.background().clone());
8985 fs.insert_file("/file.rs", Default::default()).await;
8986
8987 let project = Project::test(fs, ["/file.rs"], cx).await;
8988 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
8989 let buffer = project
8990 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
8991 .await
8992 .unwrap();
8993
8994 cx.foreground().start_waiting();
8995 let fake_server = fake_servers.next().await.unwrap();
8996
8997 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8998 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
8999 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9000 assert!(cx.read(|cx| editor.is_dirty(cx)));
9001
9002 let save = cx.update(|cx| editor.save(project.clone(), cx));
9003 fake_server
9004 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9005 assert_eq!(
9006 params.text_document.uri,
9007 lsp::Url::from_file_path("/file.rs").unwrap()
9008 );
9009 assert_eq!(params.options.tab_size, 4);
9010 Ok(Some(vec![lsp::TextEdit::new(
9011 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
9012 ", ".to_string(),
9013 )]))
9014 })
9015 .next()
9016 .await;
9017 cx.foreground().start_waiting();
9018 save.await.unwrap();
9019 assert_eq!(
9020 editor.read_with(cx, |editor, cx| editor.text(cx)),
9021 "one, two\nthree\n"
9022 );
9023 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9024
9025 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9026 assert!(cx.read(|cx| editor.is_dirty(cx)));
9027
9028 // Ensure we can still save even if formatting hangs.
9029 fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9030 assert_eq!(
9031 params.text_document.uri,
9032 lsp::Url::from_file_path("/file.rs").unwrap()
9033 );
9034 futures::future::pending::<()>().await;
9035 unreachable!()
9036 });
9037 let save = cx.update(|cx| editor.save(project.clone(), cx));
9038 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
9039 cx.foreground().start_waiting();
9040 save.await.unwrap();
9041 assert_eq!(
9042 editor.read_with(cx, |editor, cx| editor.text(cx)),
9043 "one\ntwo\nthree\n"
9044 );
9045 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9046
9047 // Set rust language override and assert overriden tabsize is sent to language server
9048 cx.update(|cx| {
9049 cx.update_global::<Settings, _, _>(|settings, _| {
9050 settings.language_overrides.insert(
9051 "Rust".into(),
9052 LanguageOverride {
9053 tab_size: Some(8),
9054 ..Default::default()
9055 },
9056 );
9057 })
9058 });
9059
9060 let save = cx.update(|cx| editor.save(project.clone(), cx));
9061 fake_server
9062 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9063 assert_eq!(
9064 params.text_document.uri,
9065 lsp::Url::from_file_path("/file.rs").unwrap()
9066 );
9067 assert_eq!(params.options.tab_size, 8);
9068 Ok(Some(vec![]))
9069 })
9070 .next()
9071 .await;
9072 cx.foreground().start_waiting();
9073 save.await.unwrap();
9074 }
9075
9076 #[gpui::test]
9077 async fn test_completion(cx: &mut gpui::TestAppContext) {
9078 cx.update(|cx| cx.set_global(Settings::test(cx)));
9079
9080 let mut language = Language::new(
9081 LanguageConfig {
9082 name: "Rust".into(),
9083 path_suffixes: vec!["rs".to_string()],
9084 ..Default::default()
9085 },
9086 Some(tree_sitter_rust::language()),
9087 );
9088 let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
9089 capabilities: lsp::ServerCapabilities {
9090 completion_provider: Some(lsp::CompletionOptions {
9091 trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
9092 ..Default::default()
9093 }),
9094 ..Default::default()
9095 },
9096 ..Default::default()
9097 });
9098
9099 let text = "
9100 one
9101 two
9102 three
9103 "
9104 .unindent();
9105
9106 let fs = FakeFs::new(cx.background().clone());
9107 fs.insert_file("/file.rs", text).await;
9108
9109 let project = Project::test(fs, ["/file.rs"], cx).await;
9110 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9111 let buffer = project
9112 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9113 .await
9114 .unwrap();
9115 let mut fake_server = fake_servers.next().await.unwrap();
9116
9117 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9118 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9119
9120 editor.update(cx, |editor, cx| {
9121 editor.project = Some(project);
9122 editor.change_selections(None, cx, |s| {
9123 s.select_ranges([Point::new(0, 3)..Point::new(0, 3)])
9124 });
9125 editor.handle_input(&Input(".".to_string()), cx);
9126 });
9127
9128 handle_completion_request(
9129 &mut fake_server,
9130 "/file.rs",
9131 Point::new(0, 4),
9132 vec![
9133 (Point::new(0, 4)..Point::new(0, 4), "first_completion"),
9134 (Point::new(0, 4)..Point::new(0, 4), "second_completion"),
9135 ],
9136 )
9137 .await;
9138 editor
9139 .condition(&cx, |editor, _| editor.context_menu_visible())
9140 .await;
9141
9142 let apply_additional_edits = editor.update(cx, |editor, cx| {
9143 editor.move_down(&MoveDown, cx);
9144 let apply_additional_edits = editor
9145 .confirm_completion(&ConfirmCompletion::default(), cx)
9146 .unwrap();
9147 assert_eq!(
9148 editor.text(cx),
9149 "
9150 one.second_completion
9151 two
9152 three
9153 "
9154 .unindent()
9155 );
9156 apply_additional_edits
9157 });
9158
9159 handle_resolve_completion_request(
9160 &mut fake_server,
9161 Some((Point::new(2, 5)..Point::new(2, 5), "\nadditional edit")),
9162 )
9163 .await;
9164 apply_additional_edits.await.unwrap();
9165 assert_eq!(
9166 editor.read_with(cx, |editor, cx| editor.text(cx)),
9167 "
9168 one.second_completion
9169 two
9170 three
9171 additional edit
9172 "
9173 .unindent()
9174 );
9175
9176 editor.update(cx, |editor, cx| {
9177 editor.change_selections(None, cx, |s| {
9178 s.select_ranges([
9179 Point::new(1, 3)..Point::new(1, 3),
9180 Point::new(2, 5)..Point::new(2, 5),
9181 ])
9182 });
9183
9184 editor.handle_input(&Input(" ".to_string()), cx);
9185 assert!(editor.context_menu.is_none());
9186 editor.handle_input(&Input("s".to_string()), cx);
9187 assert!(editor.context_menu.is_none());
9188 });
9189
9190 handle_completion_request(
9191 &mut fake_server,
9192 "/file.rs",
9193 Point::new(2, 7),
9194 vec![
9195 (Point::new(2, 6)..Point::new(2, 7), "fourth_completion"),
9196 (Point::new(2, 6)..Point::new(2, 7), "fifth_completion"),
9197 (Point::new(2, 6)..Point::new(2, 7), "sixth_completion"),
9198 ],
9199 )
9200 .await;
9201 editor
9202 .condition(&cx, |editor, _| editor.context_menu_visible())
9203 .await;
9204
9205 editor.update(cx, |editor, cx| {
9206 editor.handle_input(&Input("i".to_string()), cx);
9207 });
9208
9209 handle_completion_request(
9210 &mut fake_server,
9211 "/file.rs",
9212 Point::new(2, 8),
9213 vec![
9214 (Point::new(2, 6)..Point::new(2, 8), "fourth_completion"),
9215 (Point::new(2, 6)..Point::new(2, 8), "fifth_completion"),
9216 (Point::new(2, 6)..Point::new(2, 8), "sixth_completion"),
9217 ],
9218 )
9219 .await;
9220 editor
9221 .condition(&cx, |editor, _| editor.context_menu_visible())
9222 .await;
9223
9224 let apply_additional_edits = editor.update(cx, |editor, cx| {
9225 let apply_additional_edits = editor
9226 .confirm_completion(&ConfirmCompletion::default(), cx)
9227 .unwrap();
9228 assert_eq!(
9229 editor.text(cx),
9230 "
9231 one.second_completion
9232 two sixth_completion
9233 three sixth_completion
9234 additional edit
9235 "
9236 .unindent()
9237 );
9238 apply_additional_edits
9239 });
9240 handle_resolve_completion_request(&mut fake_server, None).await;
9241 apply_additional_edits.await.unwrap();
9242
9243 async fn handle_completion_request(
9244 fake: &mut FakeLanguageServer,
9245 path: &'static str,
9246 position: Point,
9247 completions: Vec<(Range<Point>, &'static str)>,
9248 ) {
9249 fake.handle_request::<lsp::request::Completion, _, _>(move |params, _| {
9250 let completions = completions.clone();
9251 async move {
9252 assert_eq!(
9253 params.text_document_position.text_document.uri,
9254 lsp::Url::from_file_path(path).unwrap()
9255 );
9256 assert_eq!(
9257 params.text_document_position.position,
9258 lsp::Position::new(position.row, position.column)
9259 );
9260 Ok(Some(lsp::CompletionResponse::Array(
9261 completions
9262 .iter()
9263 .map(|(range, new_text)| lsp::CompletionItem {
9264 label: new_text.to_string(),
9265 text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
9266 range: lsp::Range::new(
9267 lsp::Position::new(range.start.row, range.start.column),
9268 lsp::Position::new(range.start.row, range.start.column),
9269 ),
9270 new_text: new_text.to_string(),
9271 })),
9272 ..Default::default()
9273 })
9274 .collect(),
9275 )))
9276 }
9277 })
9278 .next()
9279 .await;
9280 }
9281
9282 async fn handle_resolve_completion_request(
9283 fake: &mut FakeLanguageServer,
9284 edit: Option<(Range<Point>, &'static str)>,
9285 ) {
9286 fake.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _| {
9287 let edit = edit.clone();
9288 async move {
9289 Ok(lsp::CompletionItem {
9290 additional_text_edits: edit.map(|(range, new_text)| {
9291 vec![lsp::TextEdit::new(
9292 lsp::Range::new(
9293 lsp::Position::new(range.start.row, range.start.column),
9294 lsp::Position::new(range.end.row, range.end.column),
9295 ),
9296 new_text.to_string(),
9297 )]
9298 }),
9299 ..Default::default()
9300 })
9301 }
9302 })
9303 .next()
9304 .await;
9305 }
9306 }
9307
9308 #[gpui::test]
9309 async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
9310 cx.update(|cx| cx.set_global(Settings::test(cx)));
9311 let language = Arc::new(Language::new(
9312 LanguageConfig {
9313 line_comment: Some("// ".to_string()),
9314 ..Default::default()
9315 },
9316 Some(tree_sitter_rust::language()),
9317 ));
9318
9319 let text = "
9320 fn a() {
9321 //b();
9322 // c();
9323 // d();
9324 }
9325 "
9326 .unindent();
9327
9328 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9329 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9330 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9331
9332 view.update(cx, |editor, cx| {
9333 // If multiple selections intersect a line, the line is only
9334 // toggled once.
9335 editor.change_selections(None, cx, |s| {
9336 s.select_display_ranges([
9337 DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
9338 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
9339 ])
9340 });
9341 editor.toggle_comments(&ToggleComments, cx);
9342 assert_eq!(
9343 editor.text(cx),
9344 "
9345 fn a() {
9346 b();
9347 c();
9348 d();
9349 }
9350 "
9351 .unindent()
9352 );
9353
9354 // The comment prefix is inserted at the same column for every line
9355 // in a selection.
9356 editor.change_selections(None, cx, |s| {
9357 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
9358 });
9359 editor.toggle_comments(&ToggleComments, cx);
9360 assert_eq!(
9361 editor.text(cx),
9362 "
9363 fn a() {
9364 // b();
9365 // c();
9366 // d();
9367 }
9368 "
9369 .unindent()
9370 );
9371
9372 // If a selection ends at the beginning of a line, that line is not toggled.
9373 editor.change_selections(None, cx, |s| {
9374 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
9375 });
9376 editor.toggle_comments(&ToggleComments, cx);
9377 assert_eq!(
9378 editor.text(cx),
9379 "
9380 fn a() {
9381 // b();
9382 c();
9383 // d();
9384 }
9385 "
9386 .unindent()
9387 );
9388 });
9389 }
9390
9391 #[gpui::test]
9392 fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
9393 cx.set_global(Settings::test(cx));
9394 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9395 let multibuffer = cx.add_model(|cx| {
9396 let mut multibuffer = MultiBuffer::new(0);
9397 multibuffer.push_excerpts(
9398 buffer.clone(),
9399 [
9400 Point::new(0, 0)..Point::new(0, 4),
9401 Point::new(1, 0)..Point::new(1, 4),
9402 ],
9403 cx,
9404 );
9405 multibuffer
9406 });
9407
9408 assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
9409
9410 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
9411 view.update(cx, |view, cx| {
9412 assert_eq!(view.text(cx), "aaaa\nbbbb");
9413 view.change_selections(None, cx, |s| {
9414 s.select_ranges([
9415 Point::new(0, 0)..Point::new(0, 0),
9416 Point::new(1, 0)..Point::new(1, 0),
9417 ])
9418 });
9419
9420 view.handle_input(&Input("X".to_string()), cx);
9421 assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
9422 assert_eq!(
9423 view.selected_ranges(cx),
9424 [
9425 Point::new(0, 1)..Point::new(0, 1),
9426 Point::new(1, 1)..Point::new(1, 1),
9427 ]
9428 )
9429 });
9430 }
9431
9432 #[gpui::test]
9433 fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
9434 cx.set_global(Settings::test(cx));
9435 let (initial_text, excerpt_ranges) = marked_text_ranges(indoc! {"
9436 [aaaa
9437 (bbbb]
9438 cccc)"});
9439 let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
9440 let multibuffer = cx.add_model(|cx| {
9441 let mut multibuffer = MultiBuffer::new(0);
9442 multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
9443 multibuffer
9444 });
9445
9446 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
9447 view.update(cx, |view, cx| {
9448 let (expected_text, selection_ranges) = marked_text_ranges(indoc! {"
9449 aaaa
9450 b|bbb
9451 b|bb|b
9452 cccc"});
9453 assert_eq!(view.text(cx), expected_text);
9454 view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
9455
9456 view.handle_input(&Input("X".to_string()), cx);
9457
9458 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
9459 aaaa
9460 bX|bbXb
9461 bX|bbX|b
9462 cccc"});
9463 assert_eq!(view.text(cx), expected_text);
9464 assert_eq!(view.selected_ranges(cx), expected_selections);
9465
9466 view.newline(&Newline, cx);
9467 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
9468 aaaa
9469 bX
9470 |bbX
9471 b
9472 bX
9473 |bbX
9474 |b
9475 cccc"});
9476 assert_eq!(view.text(cx), expected_text);
9477 assert_eq!(view.selected_ranges(cx), expected_selections);
9478 });
9479 }
9480
9481 #[gpui::test]
9482 fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
9483 cx.set_global(Settings::test(cx));
9484 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9485 let mut excerpt1_id = None;
9486 let multibuffer = cx.add_model(|cx| {
9487 let mut multibuffer = MultiBuffer::new(0);
9488 excerpt1_id = multibuffer
9489 .push_excerpts(
9490 buffer.clone(),
9491 [
9492 Point::new(0, 0)..Point::new(1, 4),
9493 Point::new(1, 0)..Point::new(2, 4),
9494 ],
9495 cx,
9496 )
9497 .into_iter()
9498 .next();
9499 multibuffer
9500 });
9501 assert_eq!(
9502 multibuffer.read(cx).read(cx).text(),
9503 "aaaa\nbbbb\nbbbb\ncccc"
9504 );
9505 let (_, editor) = cx.add_window(Default::default(), |cx| {
9506 let mut editor = build_editor(multibuffer.clone(), cx);
9507 let snapshot = editor.snapshot(cx);
9508 editor.change_selections(None, cx, |s| {
9509 s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
9510 });
9511 editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
9512 assert_eq!(
9513 editor.selected_ranges(cx),
9514 [
9515 Point::new(1, 3)..Point::new(1, 3),
9516 Point::new(2, 1)..Point::new(2, 1),
9517 ]
9518 );
9519 editor
9520 });
9521
9522 // Refreshing selections is a no-op when excerpts haven't changed.
9523 editor.update(cx, |editor, cx| {
9524 editor.change_selections(None, cx, |s| {
9525 s.refresh();
9526 });
9527 assert_eq!(
9528 editor.selected_ranges(cx),
9529 [
9530 Point::new(1, 3)..Point::new(1, 3),
9531 Point::new(2, 1)..Point::new(2, 1),
9532 ]
9533 );
9534 });
9535
9536 multibuffer.update(cx, |multibuffer, cx| {
9537 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
9538 });
9539 editor.update(cx, |editor, cx| {
9540 // Removing an excerpt causes the first selection to become degenerate.
9541 assert_eq!(
9542 editor.selected_ranges(cx),
9543 [
9544 Point::new(0, 0)..Point::new(0, 0),
9545 Point::new(0, 1)..Point::new(0, 1)
9546 ]
9547 );
9548
9549 // Refreshing selections will relocate the first selection to the original buffer
9550 // location.
9551 editor.change_selections(None, cx, |s| {
9552 s.refresh();
9553 });
9554 assert_eq!(
9555 editor.selected_ranges(cx),
9556 [
9557 Point::new(0, 1)..Point::new(0, 1),
9558 Point::new(0, 3)..Point::new(0, 3)
9559 ]
9560 );
9561 assert!(editor.selections.pending_anchor().is_some());
9562 });
9563 }
9564
9565 #[gpui::test]
9566 fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
9567 cx.set_global(Settings::test(cx));
9568 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9569 let mut excerpt1_id = None;
9570 let multibuffer = cx.add_model(|cx| {
9571 let mut multibuffer = MultiBuffer::new(0);
9572 excerpt1_id = multibuffer
9573 .push_excerpts(
9574 buffer.clone(),
9575 [
9576 Point::new(0, 0)..Point::new(1, 4),
9577 Point::new(1, 0)..Point::new(2, 4),
9578 ],
9579 cx,
9580 )
9581 .into_iter()
9582 .next();
9583 multibuffer
9584 });
9585 assert_eq!(
9586 multibuffer.read(cx).read(cx).text(),
9587 "aaaa\nbbbb\nbbbb\ncccc"
9588 );
9589 let (_, editor) = cx.add_window(Default::default(), |cx| {
9590 let mut editor = build_editor(multibuffer.clone(), cx);
9591 let snapshot = editor.snapshot(cx);
9592 editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
9593 assert_eq!(
9594 editor.selected_ranges(cx),
9595 [Point::new(1, 3)..Point::new(1, 3)]
9596 );
9597 editor
9598 });
9599
9600 multibuffer.update(cx, |multibuffer, cx| {
9601 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
9602 });
9603 editor.update(cx, |editor, cx| {
9604 assert_eq!(
9605 editor.selected_ranges(cx),
9606 [Point::new(0, 0)..Point::new(0, 0)]
9607 );
9608
9609 // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
9610 editor.change_selections(None, cx, |s| {
9611 s.refresh();
9612 });
9613 assert_eq!(
9614 editor.selected_ranges(cx),
9615 [Point::new(0, 3)..Point::new(0, 3)]
9616 );
9617 assert!(editor.selections.pending_anchor().is_some());
9618 });
9619 }
9620
9621 #[gpui::test]
9622 async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
9623 cx.update(|cx| cx.set_global(Settings::test(cx)));
9624 let language = Arc::new(Language::new(
9625 LanguageConfig {
9626 brackets: vec![
9627 BracketPair {
9628 start: "{".to_string(),
9629 end: "}".to_string(),
9630 close: true,
9631 newline: true,
9632 },
9633 BracketPair {
9634 start: "/* ".to_string(),
9635 end: " */".to_string(),
9636 close: true,
9637 newline: true,
9638 },
9639 ],
9640 ..Default::default()
9641 },
9642 Some(tree_sitter_rust::language()),
9643 ));
9644
9645 let text = concat!(
9646 "{ }\n", // Suppress rustfmt
9647 " x\n", //
9648 " /* */\n", //
9649 "x\n", //
9650 "{{} }\n", //
9651 );
9652
9653 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9654 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9655 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9656 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9657 .await;
9658
9659 view.update(cx, |view, cx| {
9660 view.change_selections(None, cx, |s| {
9661 s.select_display_ranges([
9662 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
9663 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
9664 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
9665 ])
9666 });
9667 view.newline(&Newline, cx);
9668
9669 assert_eq!(
9670 view.buffer().read(cx).read(cx).text(),
9671 concat!(
9672 "{ \n", // Suppress rustfmt
9673 "\n", //
9674 "}\n", //
9675 " x\n", //
9676 " /* \n", //
9677 " \n", //
9678 " */\n", //
9679 "x\n", //
9680 "{{} \n", //
9681 "}\n", //
9682 )
9683 );
9684 });
9685 }
9686
9687 #[gpui::test]
9688 fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
9689 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
9690
9691 cx.set_global(Settings::test(cx));
9692 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
9693
9694 editor.update(cx, |editor, cx| {
9695 struct Type1;
9696 struct Type2;
9697
9698 let buffer = buffer.read(cx).snapshot(cx);
9699
9700 let anchor_range = |range: Range<Point>| {
9701 buffer.anchor_after(range.start)..buffer.anchor_after(range.end)
9702 };
9703
9704 editor.highlight_background::<Type1>(
9705 vec![
9706 anchor_range(Point::new(2, 1)..Point::new(2, 3)),
9707 anchor_range(Point::new(4, 2)..Point::new(4, 4)),
9708 anchor_range(Point::new(6, 3)..Point::new(6, 5)),
9709 anchor_range(Point::new(8, 4)..Point::new(8, 6)),
9710 ],
9711 |_| Color::red(),
9712 cx,
9713 );
9714 editor.highlight_background::<Type2>(
9715 vec![
9716 anchor_range(Point::new(3, 2)..Point::new(3, 5)),
9717 anchor_range(Point::new(5, 3)..Point::new(5, 6)),
9718 anchor_range(Point::new(7, 4)..Point::new(7, 7)),
9719 anchor_range(Point::new(9, 5)..Point::new(9, 8)),
9720 ],
9721 |_| Color::green(),
9722 cx,
9723 );
9724
9725 let snapshot = editor.snapshot(cx);
9726 let mut highlighted_ranges = editor.background_highlights_in_range(
9727 anchor_range(Point::new(3, 4)..Point::new(7, 4)),
9728 &snapshot,
9729 cx.global::<Settings>().theme.as_ref(),
9730 );
9731 // Enforce a consistent ordering based on color without relying on the ordering of the
9732 // highlight's `TypeId` which is non-deterministic.
9733 highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
9734 assert_eq!(
9735 highlighted_ranges,
9736 &[
9737 (
9738 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
9739 Color::green(),
9740 ),
9741 (
9742 DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
9743 Color::green(),
9744 ),
9745 (
9746 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
9747 Color::red(),
9748 ),
9749 (
9750 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
9751 Color::red(),
9752 ),
9753 ]
9754 );
9755 assert_eq!(
9756 editor.background_highlights_in_range(
9757 anchor_range(Point::new(5, 6)..Point::new(6, 4)),
9758 &snapshot,
9759 cx.global::<Settings>().theme.as_ref(),
9760 ),
9761 &[(
9762 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
9763 Color::red(),
9764 )]
9765 );
9766 });
9767 }
9768
9769 #[gpui::test]
9770 fn test_following(cx: &mut gpui::MutableAppContext) {
9771 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
9772
9773 cx.set_global(Settings::test(cx));
9774
9775 let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
9776 let (_, follower) = cx.add_window(
9777 WindowOptions {
9778 bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
9779 ..Default::default()
9780 },
9781 |cx| build_editor(buffer.clone(), cx),
9782 );
9783
9784 let pending_update = Rc::new(RefCell::new(None));
9785 follower.update(cx, {
9786 let update = pending_update.clone();
9787 |_, cx| {
9788 cx.subscribe(&leader, move |_, leader, event, cx| {
9789 leader
9790 .read(cx)
9791 .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
9792 })
9793 .detach();
9794 }
9795 });
9796
9797 // Update the selections only
9798 leader.update(cx, |leader, cx| {
9799 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
9800 });
9801 follower.update(cx, |follower, cx| {
9802 follower
9803 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
9804 .unwrap();
9805 });
9806 assert_eq!(follower.read(cx).selected_ranges(cx), vec![1..1]);
9807
9808 // Update the scroll position only
9809 leader.update(cx, |leader, cx| {
9810 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
9811 });
9812 follower.update(cx, |follower, cx| {
9813 follower
9814 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
9815 .unwrap();
9816 });
9817 assert_eq!(
9818 follower.update(cx, |follower, cx| follower.scroll_position(cx)),
9819 vec2f(1.5, 3.5)
9820 );
9821
9822 // Update the selections and scroll position
9823 leader.update(cx, |leader, cx| {
9824 leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
9825 leader.request_autoscroll(Autoscroll::Newest, cx);
9826 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
9827 });
9828 follower.update(cx, |follower, cx| {
9829 let initial_scroll_position = follower.scroll_position(cx);
9830 follower
9831 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
9832 .unwrap();
9833 assert_eq!(follower.scroll_position(cx), initial_scroll_position);
9834 assert!(follower.autoscroll_request.is_some());
9835 });
9836 assert_eq!(follower.read(cx).selected_ranges(cx), vec![0..0]);
9837
9838 // Creating a pending selection that precedes another selection
9839 leader.update(cx, |leader, cx| {
9840 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
9841 leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
9842 });
9843 follower.update(cx, |follower, cx| {
9844 follower
9845 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
9846 .unwrap();
9847 });
9848 assert_eq!(follower.read(cx).selected_ranges(cx), vec![0..0, 1..1]);
9849
9850 // Extend the pending selection so that it surrounds another selection
9851 leader.update(cx, |leader, cx| {
9852 leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
9853 });
9854 follower.update(cx, |follower, cx| {
9855 follower
9856 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
9857 .unwrap();
9858 });
9859 assert_eq!(follower.read(cx).selected_ranges(cx), vec![0..2]);
9860 }
9861
9862 #[test]
9863 fn test_combine_syntax_and_fuzzy_match_highlights() {
9864 let string = "abcdefghijklmnop";
9865 let syntax_ranges = [
9866 (
9867 0..3,
9868 HighlightStyle {
9869 color: Some(Color::red()),
9870 ..Default::default()
9871 },
9872 ),
9873 (
9874 4..8,
9875 HighlightStyle {
9876 color: Some(Color::green()),
9877 ..Default::default()
9878 },
9879 ),
9880 ];
9881 let match_indices = [4, 6, 7, 8];
9882 assert_eq!(
9883 combine_syntax_and_fuzzy_match_highlights(
9884 &string,
9885 Default::default(),
9886 syntax_ranges.into_iter(),
9887 &match_indices,
9888 ),
9889 &[
9890 (
9891 0..3,
9892 HighlightStyle {
9893 color: Some(Color::red()),
9894 ..Default::default()
9895 },
9896 ),
9897 (
9898 4..5,
9899 HighlightStyle {
9900 color: Some(Color::green()),
9901 weight: Some(fonts::Weight::BOLD),
9902 ..Default::default()
9903 },
9904 ),
9905 (
9906 5..6,
9907 HighlightStyle {
9908 color: Some(Color::green()),
9909 ..Default::default()
9910 },
9911 ),
9912 (
9913 6..8,
9914 HighlightStyle {
9915 color: Some(Color::green()),
9916 weight: Some(fonts::Weight::BOLD),
9917 ..Default::default()
9918 },
9919 ),
9920 (
9921 8..9,
9922 HighlightStyle {
9923 weight: Some(fonts::Weight::BOLD),
9924 ..Default::default()
9925 },
9926 ),
9927 ]
9928 );
9929 }
9930
9931 fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
9932 let point = DisplayPoint::new(row as u32, column as u32);
9933 point..point
9934 }
9935
9936 fn build_editor(buffer: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Editor>) -> Editor {
9937 Editor::new(EditorMode::Full, buffer, None, None, cx)
9938 }
9939
9940 fn assert_selection_ranges(
9941 marked_text: &str,
9942 selection_marker_pairs: Vec<(char, char)>,
9943 view: &mut Editor,
9944 cx: &mut ViewContext<Editor>,
9945 ) {
9946 let snapshot = view.snapshot(cx).display_snapshot;
9947 let mut marker_chars = Vec::new();
9948 for (start, end) in selection_marker_pairs.iter() {
9949 marker_chars.push(*start);
9950 marker_chars.push(*end);
9951 }
9952 let (_, markers) = marked_text_by(marked_text, marker_chars);
9953 let asserted_ranges: Vec<Range<DisplayPoint>> = selection_marker_pairs
9954 .iter()
9955 .map(|(start, end)| {
9956 let start = markers.get(start).unwrap()[0].to_display_point(&snapshot);
9957 let end = markers.get(end).unwrap()[0].to_display_point(&snapshot);
9958 start..end
9959 })
9960 .collect();
9961 assert_eq!(
9962 view.selected_display_ranges(cx),
9963 &asserted_ranges[..],
9964 "Assert selections are {}",
9965 marked_text
9966 );
9967 }
9968}
9969
9970trait RangeExt<T> {
9971 fn sorted(&self) -> Range<T>;
9972 fn to_inclusive(&self) -> RangeInclusive<T>;
9973}
9974
9975impl<T: Ord + Clone> RangeExt<T> for Range<T> {
9976 fn sorted(&self) -> Self {
9977 cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone()
9978 }
9979
9980 fn to_inclusive(&self) -> RangeInclusive<T> {
9981 self.start.clone()..=self.end.clone()
9982 }
9983}