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