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