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