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(Some(NavigationData {
4069 cursor_anchor: position,
4070 cursor_position: point,
4071 scroll_position: self.scroll_position,
4072 scroll_top_anchor: self.scroll_top_anchor.clone(),
4073 scroll_top_row,
4074 }));
4075 }
4076 }
4077
4078 pub fn select_to_end(&mut self, _: &SelectToEnd, cx: &mut ViewContext<Self>) {
4079 let buffer = self.buffer.read(cx).snapshot(cx);
4080 let mut selection = self.selections.first::<usize>(cx);
4081 selection.set_head(buffer.len(), SelectionGoal::None);
4082 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4083 s.select(vec![selection]);
4084 });
4085 }
4086
4087 pub fn select_all(&mut self, _: &SelectAll, cx: &mut ViewContext<Self>) {
4088 let end = self.buffer.read(cx).read(cx).len();
4089 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4090 s.select_ranges(vec![0..end]);
4091 });
4092 }
4093
4094 pub fn select_line(&mut self, _: &SelectLine, cx: &mut ViewContext<Self>) {
4095 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4096 let mut selections = self.selections.all::<Point>(cx);
4097 let max_point = display_map.buffer_snapshot.max_point();
4098 for selection in &mut selections {
4099 let rows = selection.spanned_rows(true, &display_map);
4100 selection.start = Point::new(rows.start, 0);
4101 selection.end = cmp::min(max_point, Point::new(rows.end, 0));
4102 selection.reversed = false;
4103 }
4104 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4105 s.select(selections);
4106 });
4107 }
4108
4109 pub fn split_selection_into_lines(
4110 &mut self,
4111 _: &SplitSelectionIntoLines,
4112 cx: &mut ViewContext<Self>,
4113 ) {
4114 let mut to_unfold = Vec::new();
4115 let mut new_selection_ranges = Vec::new();
4116 {
4117 let selections = self.selections.all::<Point>(cx);
4118 let buffer = self.buffer.read(cx).read(cx);
4119 for selection in selections {
4120 for row in selection.start.row..selection.end.row {
4121 let cursor = Point::new(row, buffer.line_len(row));
4122 new_selection_ranges.push(cursor..cursor);
4123 }
4124 new_selection_ranges.push(selection.end..selection.end);
4125 to_unfold.push(selection.start..selection.end);
4126 }
4127 }
4128 self.unfold_ranges(to_unfold, true, cx);
4129 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4130 s.select_ranges(new_selection_ranges);
4131 });
4132 }
4133
4134 pub fn add_selection_above(&mut self, _: &AddSelectionAbove, cx: &mut ViewContext<Self>) {
4135 self.add_selection(true, cx);
4136 }
4137
4138 pub fn add_selection_below(&mut self, _: &AddSelectionBelow, cx: &mut ViewContext<Self>) {
4139 self.add_selection(false, cx);
4140 }
4141
4142 fn add_selection(&mut self, above: bool, cx: &mut ViewContext<Self>) {
4143 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4144 let mut selections = self.selections.all::<Point>(cx);
4145 let mut state = self.add_selections_state.take().unwrap_or_else(|| {
4146 let oldest_selection = selections.iter().min_by_key(|s| s.id).unwrap().clone();
4147 let range = oldest_selection.display_range(&display_map).sorted();
4148 let columns = cmp::min(range.start.column(), range.end.column())
4149 ..cmp::max(range.start.column(), range.end.column());
4150
4151 selections.clear();
4152 let mut stack = Vec::new();
4153 for row in range.start.row()..=range.end.row() {
4154 if let Some(selection) = self.selections.build_columnar_selection(
4155 &display_map,
4156 row,
4157 &columns,
4158 oldest_selection.reversed,
4159 ) {
4160 stack.push(selection.id);
4161 selections.push(selection);
4162 }
4163 }
4164
4165 if above {
4166 stack.reverse();
4167 }
4168
4169 AddSelectionsState { above, stack }
4170 });
4171
4172 let last_added_selection = *state.stack.last().unwrap();
4173 let mut new_selections = Vec::new();
4174 if above == state.above {
4175 let end_row = if above {
4176 0
4177 } else {
4178 display_map.max_point().row()
4179 };
4180
4181 'outer: for selection in selections {
4182 if selection.id == last_added_selection {
4183 let range = selection.display_range(&display_map).sorted();
4184 debug_assert_eq!(range.start.row(), range.end.row());
4185 let mut row = range.start.row();
4186 let columns = if let SelectionGoal::ColumnRange { start, end } = selection.goal
4187 {
4188 start..end
4189 } else {
4190 cmp::min(range.start.column(), range.end.column())
4191 ..cmp::max(range.start.column(), range.end.column())
4192 };
4193
4194 while row != end_row {
4195 if above {
4196 row -= 1;
4197 } else {
4198 row += 1;
4199 }
4200
4201 if let Some(new_selection) = self.selections.build_columnar_selection(
4202 &display_map,
4203 row,
4204 &columns,
4205 selection.reversed,
4206 ) {
4207 state.stack.push(new_selection.id);
4208 if above {
4209 new_selections.push(new_selection);
4210 new_selections.push(selection);
4211 } else {
4212 new_selections.push(selection);
4213 new_selections.push(new_selection);
4214 }
4215
4216 continue 'outer;
4217 }
4218 }
4219 }
4220
4221 new_selections.push(selection);
4222 }
4223 } else {
4224 new_selections = selections;
4225 new_selections.retain(|s| s.id != last_added_selection);
4226 state.stack.pop();
4227 }
4228
4229 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4230 s.select(new_selections);
4231 });
4232 if state.stack.len() > 1 {
4233 self.add_selections_state = Some(state);
4234 }
4235 }
4236
4237 pub fn select_next(&mut self, action: &SelectNext, cx: &mut ViewContext<Self>) {
4238 self.push_to_selection_history();
4239 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4240 let buffer = &display_map.buffer_snapshot;
4241 let mut selections = self.selections.all::<usize>(cx);
4242 if let Some(mut select_next_state) = self.select_next_state.take() {
4243 let query = &select_next_state.query;
4244 if !select_next_state.done {
4245 let first_selection = selections.iter().min_by_key(|s| s.id).unwrap();
4246 let last_selection = selections.iter().max_by_key(|s| s.id).unwrap();
4247 let mut next_selected_range = None;
4248
4249 let bytes_after_last_selection =
4250 buffer.bytes_in_range(last_selection.end..buffer.len());
4251 let bytes_before_first_selection = buffer.bytes_in_range(0..first_selection.start);
4252 let query_matches = query
4253 .stream_find_iter(bytes_after_last_selection)
4254 .map(|result| (last_selection.end, result))
4255 .chain(
4256 query
4257 .stream_find_iter(bytes_before_first_selection)
4258 .map(|result| (0, result)),
4259 );
4260 for (start_offset, query_match) in query_matches {
4261 let query_match = query_match.unwrap(); // can only fail due to I/O
4262 let offset_range =
4263 start_offset + query_match.start()..start_offset + query_match.end();
4264 let display_range = offset_range.start.to_display_point(&display_map)
4265 ..offset_range.end.to_display_point(&display_map);
4266
4267 if !select_next_state.wordwise
4268 || (!movement::is_inside_word(&display_map, display_range.start)
4269 && !movement::is_inside_word(&display_map, display_range.end))
4270 {
4271 next_selected_range = Some(offset_range);
4272 break;
4273 }
4274 }
4275
4276 if let Some(next_selected_range) = next_selected_range {
4277 self.unfold_ranges([next_selected_range.clone()], false, cx);
4278 self.change_selections(Some(Autoscroll::Newest), cx, |s| {
4279 if action.replace_newest {
4280 s.delete(s.newest_anchor().id);
4281 }
4282 s.insert_range(next_selected_range);
4283 });
4284 } else {
4285 select_next_state.done = true;
4286 }
4287 }
4288
4289 self.select_next_state = Some(select_next_state);
4290 } else if selections.len() == 1 {
4291 let selection = selections.last_mut().unwrap();
4292 if selection.start == selection.end {
4293 let word_range = movement::surrounding_word(
4294 &display_map,
4295 selection.start.to_display_point(&display_map),
4296 );
4297 selection.start = word_range.start.to_offset(&display_map, Bias::Left);
4298 selection.end = word_range.end.to_offset(&display_map, Bias::Left);
4299 selection.goal = SelectionGoal::None;
4300 selection.reversed = false;
4301
4302 let query = buffer
4303 .text_for_range(selection.start..selection.end)
4304 .collect::<String>();
4305 let select_state = SelectNextState {
4306 query: AhoCorasick::new_auto_configured(&[query]),
4307 wordwise: true,
4308 done: false,
4309 };
4310 self.unfold_ranges([selection.start..selection.end], false, cx);
4311 self.change_selections(Some(Autoscroll::Newest), cx, |s| {
4312 s.select(selections);
4313 });
4314 self.select_next_state = Some(select_state);
4315 } else {
4316 let query = buffer
4317 .text_for_range(selection.start..selection.end)
4318 .collect::<String>();
4319 self.select_next_state = Some(SelectNextState {
4320 query: AhoCorasick::new_auto_configured(&[query]),
4321 wordwise: false,
4322 done: false,
4323 });
4324 self.select_next(action, cx);
4325 }
4326 }
4327 }
4328
4329 pub fn toggle_comments(&mut self, _: &ToggleComments, cx: &mut ViewContext<Self>) {
4330 self.transact(cx, |this, cx| {
4331 let mut selections = this.selections.all::<Point>(cx);
4332 let mut all_selection_lines_are_comments = true;
4333 let mut edit_ranges = Vec::new();
4334 let mut last_toggled_row = None;
4335 this.buffer.update(cx, |buffer, cx| {
4336 // TODO: Handle selections that cross excerpts
4337 for selection in &mut selections {
4338 // Get the line comment prefix. Split its trailing whitespace into a separate string,
4339 // as that portion won't be used for detecting if a line is a comment.
4340 let full_comment_prefix: Arc<str> = if let Some(prefix) = buffer
4341 .language_at(selection.start, cx)
4342 .and_then(|l| l.line_comment_prefix())
4343 {
4344 prefix.into()
4345 } else {
4346 return;
4347 };
4348 let comment_prefix = full_comment_prefix.trim_end_matches(' ');
4349 let comment_prefix_whitespace = &full_comment_prefix[comment_prefix.len()..];
4350 edit_ranges.clear();
4351 let snapshot = buffer.snapshot(cx);
4352
4353 let end_row =
4354 if selection.end.row > selection.start.row && selection.end.column == 0 {
4355 selection.end.row
4356 } else {
4357 selection.end.row + 1
4358 };
4359
4360 for row in selection.start.row..end_row {
4361 // If multiple selections contain a given row, avoid processing that
4362 // row more than once.
4363 if last_toggled_row == Some(row) {
4364 continue;
4365 } else {
4366 last_toggled_row = Some(row);
4367 }
4368
4369 if snapshot.is_line_blank(row) {
4370 continue;
4371 }
4372
4373 let start = Point::new(row, snapshot.indent_size_for_line(row).len);
4374 let mut line_bytes = snapshot
4375 .bytes_in_range(start..snapshot.max_point())
4376 .flatten()
4377 .copied();
4378
4379 // If this line currently begins with the line comment prefix, then record
4380 // the range containing the prefix.
4381 if all_selection_lines_are_comments
4382 && line_bytes
4383 .by_ref()
4384 .take(comment_prefix.len())
4385 .eq(comment_prefix.bytes())
4386 {
4387 // Include any whitespace that matches the comment prefix.
4388 let matching_whitespace_len = line_bytes
4389 .zip(comment_prefix_whitespace.bytes())
4390 .take_while(|(a, b)| a == b)
4391 .count()
4392 as u32;
4393 let end = Point::new(
4394 row,
4395 start.column
4396 + comment_prefix.len() as u32
4397 + matching_whitespace_len,
4398 );
4399 edit_ranges.push(start..end);
4400 }
4401 // If this line does not begin with the line comment prefix, then record
4402 // the position where the prefix should be inserted.
4403 else {
4404 all_selection_lines_are_comments = false;
4405 edit_ranges.push(start..start);
4406 }
4407 }
4408
4409 if !edit_ranges.is_empty() {
4410 if all_selection_lines_are_comments {
4411 let empty_str: Arc<str> = "".into();
4412 buffer.edit(
4413 edit_ranges
4414 .iter()
4415 .cloned()
4416 .map(|range| (range, empty_str.clone())),
4417 cx,
4418 );
4419 } else {
4420 let min_column =
4421 edit_ranges.iter().map(|r| r.start.column).min().unwrap();
4422 let edits = edit_ranges.iter().map(|range| {
4423 let position = Point::new(range.start.row, min_column);
4424 (position..position, full_comment_prefix.clone())
4425 });
4426 buffer.edit(edits, cx);
4427 }
4428 }
4429 }
4430 });
4431
4432 let selections = this.selections.all::<usize>(cx);
4433 this.change_selections(Some(Autoscroll::Fit), cx, |s| s.select(selections));
4434 });
4435 }
4436
4437 pub fn select_larger_syntax_node(
4438 &mut self,
4439 _: &SelectLargerSyntaxNode,
4440 cx: &mut ViewContext<Self>,
4441 ) {
4442 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
4443 let buffer = self.buffer.read(cx).snapshot(cx);
4444 let old_selections = self.selections.all::<usize>(cx).into_boxed_slice();
4445
4446 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
4447 let mut selected_larger_node = false;
4448 let new_selections = old_selections
4449 .iter()
4450 .map(|selection| {
4451 let old_range = selection.start..selection.end;
4452 let mut new_range = old_range.clone();
4453 while let Some(containing_range) =
4454 buffer.range_for_syntax_ancestor(new_range.clone())
4455 {
4456 new_range = containing_range;
4457 if !display_map.intersects_fold(new_range.start)
4458 && !display_map.intersects_fold(new_range.end)
4459 {
4460 break;
4461 }
4462 }
4463
4464 selected_larger_node |= new_range != old_range;
4465 Selection {
4466 id: selection.id,
4467 start: new_range.start,
4468 end: new_range.end,
4469 goal: SelectionGoal::None,
4470 reversed: selection.reversed,
4471 }
4472 })
4473 .collect::<Vec<_>>();
4474
4475 if selected_larger_node {
4476 stack.push(old_selections);
4477 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4478 s.select(new_selections);
4479 });
4480 }
4481 self.select_larger_syntax_node_stack = stack;
4482 }
4483
4484 pub fn select_smaller_syntax_node(
4485 &mut self,
4486 _: &SelectSmallerSyntaxNode,
4487 cx: &mut ViewContext<Self>,
4488 ) {
4489 let mut stack = mem::take(&mut self.select_larger_syntax_node_stack);
4490 if let Some(selections) = stack.pop() {
4491 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4492 s.select(selections.to_vec());
4493 });
4494 }
4495 self.select_larger_syntax_node_stack = stack;
4496 }
4497
4498 pub fn move_to_enclosing_bracket(
4499 &mut self,
4500 _: &MoveToEnclosingBracket,
4501 cx: &mut ViewContext<Self>,
4502 ) {
4503 let buffer = self.buffer.read(cx).snapshot(cx);
4504 let mut selections = self.selections.all::<usize>(cx);
4505 for selection in &mut selections {
4506 if let Some((open_range, close_range)) =
4507 buffer.enclosing_bracket_ranges(selection.start..selection.end)
4508 {
4509 let close_range = close_range.to_inclusive();
4510 let destination = if close_range.contains(&selection.start)
4511 && close_range.contains(&selection.end)
4512 {
4513 open_range.end
4514 } else {
4515 *close_range.start()
4516 };
4517 selection.start = destination;
4518 selection.end = destination;
4519 }
4520 }
4521
4522 self.change_selections(Some(Autoscroll::Fit), cx, |s| {
4523 s.select(selections);
4524 });
4525 }
4526
4527 pub fn undo_selection(&mut self, _: &UndoSelection, cx: &mut ViewContext<Self>) {
4528 self.end_selection(cx);
4529 self.selection_history.mode = SelectionHistoryMode::Undoing;
4530 if let Some(entry) = self.selection_history.undo_stack.pop_back() {
4531 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
4532 self.select_next_state = entry.select_next_state;
4533 self.add_selections_state = entry.add_selections_state;
4534 self.request_autoscroll(Autoscroll::Newest, cx);
4535 }
4536 self.selection_history.mode = SelectionHistoryMode::Normal;
4537 }
4538
4539 pub fn redo_selection(&mut self, _: &RedoSelection, cx: &mut ViewContext<Self>) {
4540 self.end_selection(cx);
4541 self.selection_history.mode = SelectionHistoryMode::Redoing;
4542 if let Some(entry) = self.selection_history.redo_stack.pop_back() {
4543 self.change_selections(None, cx, |s| s.select_anchors(entry.selections.to_vec()));
4544 self.select_next_state = entry.select_next_state;
4545 self.add_selections_state = entry.add_selections_state;
4546 self.request_autoscroll(Autoscroll::Newest, cx);
4547 }
4548 self.selection_history.mode = SelectionHistoryMode::Normal;
4549 }
4550
4551 fn go_to_next_diagnostic(&mut self, _: &GoToNextDiagnostic, cx: &mut ViewContext<Self>) {
4552 self.go_to_diagnostic(Direction::Next, cx)
4553 }
4554
4555 fn go_to_prev_diagnostic(&mut self, _: &GoToPrevDiagnostic, cx: &mut ViewContext<Self>) {
4556 self.go_to_diagnostic(Direction::Prev, cx)
4557 }
4558
4559 pub fn go_to_diagnostic(&mut self, direction: Direction, cx: &mut ViewContext<Self>) {
4560 let buffer = self.buffer.read(cx).snapshot(cx);
4561 let selection = self.selections.newest::<usize>(cx);
4562 let mut active_primary_range = self.active_diagnostics.as_ref().map(|active_diagnostics| {
4563 active_diagnostics
4564 .primary_range
4565 .to_offset(&buffer)
4566 .to_inclusive()
4567 });
4568 let mut search_start = if let Some(active_primary_range) = active_primary_range.as_ref() {
4569 if active_primary_range.contains(&selection.head()) {
4570 *active_primary_range.end()
4571 } else {
4572 selection.head()
4573 }
4574 } else {
4575 selection.head()
4576 };
4577
4578 loop {
4579 let mut diagnostics = if direction == Direction::Prev {
4580 buffer.diagnostics_in_range::<_, usize>(0..search_start, true)
4581 } else {
4582 buffer.diagnostics_in_range::<_, usize>(search_start..buffer.len(), false)
4583 };
4584 let group = diagnostics.find_map(|entry| {
4585 if entry.diagnostic.is_primary
4586 && entry.diagnostic.severity <= DiagnosticSeverity::WARNING
4587 && !entry.range.is_empty()
4588 && Some(entry.range.end) != active_primary_range.as_ref().map(|r| *r.end())
4589 {
4590 Some((entry.range, entry.diagnostic.group_id))
4591 } else {
4592 None
4593 }
4594 });
4595
4596 if let Some((primary_range, group_id)) = group {
4597 self.activate_diagnostics(group_id, cx);
4598 self.change_selections(Some(Autoscroll::Center), cx, |s| {
4599 s.select(vec![Selection {
4600 id: selection.id,
4601 start: primary_range.start,
4602 end: primary_range.start,
4603 reversed: false,
4604 goal: SelectionGoal::None,
4605 }]);
4606 });
4607 break;
4608 } else {
4609 // Cycle around to the start of the buffer, potentially moving back to the start of
4610 // the currently active diagnostic.
4611 active_primary_range.take();
4612 if direction == Direction::Prev {
4613 if search_start == buffer.len() {
4614 break;
4615 } else {
4616 search_start = buffer.len();
4617 }
4618 } else {
4619 if search_start == 0 {
4620 break;
4621 } else {
4622 search_start = 0;
4623 }
4624 }
4625 }
4626 }
4627 }
4628
4629 pub fn go_to_definition(
4630 workspace: &mut Workspace,
4631 _: &GoToDefinition,
4632 cx: &mut ViewContext<Workspace>,
4633 ) {
4634 let active_item = workspace.active_item(cx);
4635 let editor_handle = if let Some(editor) = active_item
4636 .as_ref()
4637 .and_then(|item| item.act_as::<Self>(cx))
4638 {
4639 editor
4640 } else {
4641 return;
4642 };
4643
4644 let editor = editor_handle.read(cx);
4645 let buffer = editor.buffer.read(cx);
4646 let head = editor.selections.newest::<usize>(cx).head();
4647 let (buffer, head) = if let Some(text_anchor) = buffer.text_anchor_for_position(head, cx) {
4648 text_anchor
4649 } else {
4650 return;
4651 };
4652
4653 let project = workspace.project().clone();
4654 let definitions = project.update(cx, |project, cx| project.definition(&buffer, head, cx));
4655 cx.spawn(|workspace, mut cx| async move {
4656 let definitions = definitions.await?;
4657 workspace.update(&mut cx, |workspace, cx| {
4658 Editor::navigate_to_definitions(workspace, editor_handle, definitions, cx);
4659 });
4660
4661 Ok::<(), anyhow::Error>(())
4662 })
4663 .detach_and_log_err(cx);
4664 }
4665
4666 pub fn navigate_to_definitions(
4667 workspace: &mut Workspace,
4668 editor_handle: ViewHandle<Editor>,
4669 definitions: Vec<LocationLink>,
4670 cx: &mut ViewContext<Workspace>,
4671 ) {
4672 let nav_history = workspace.active_pane().read(cx).nav_history().clone();
4673 for definition in definitions {
4674 let range = definition
4675 .target
4676 .range
4677 .to_offset(definition.target.buffer.read(cx));
4678
4679 let target_editor_handle = workspace.open_project_item(definition.target.buffer, cx);
4680 target_editor_handle.update(cx, |target_editor, cx| {
4681 // When selecting a definition in a different buffer, disable the nav history
4682 // to avoid creating a history entry at the previous cursor location.
4683 if editor_handle != target_editor_handle {
4684 nav_history.borrow_mut().disable();
4685 }
4686 target_editor.change_selections(Some(Autoscroll::Center), cx, |s| {
4687 s.select_ranges([range]);
4688 });
4689
4690 nav_history.borrow_mut().enable();
4691 });
4692 }
4693 }
4694
4695 pub fn find_all_references(
4696 workspace: &mut Workspace,
4697 _: &FindAllReferences,
4698 cx: &mut ViewContext<Workspace>,
4699 ) -> Option<Task<Result<()>>> {
4700 let active_item = workspace.active_item(cx)?;
4701 let editor_handle = active_item.act_as::<Self>(cx)?;
4702
4703 let editor = editor_handle.read(cx);
4704 let buffer = editor.buffer.read(cx);
4705 let head = editor.selections.newest::<usize>(cx).head();
4706 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
4707 let replica_id = editor.replica_id(cx);
4708
4709 let project = workspace.project().clone();
4710 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
4711 Some(cx.spawn(|workspace, mut cx| async move {
4712 let mut locations = references.await?;
4713 if locations.is_empty() {
4714 return Ok(());
4715 }
4716
4717 locations.sort_by_key(|location| location.buffer.id());
4718 let mut locations = locations.into_iter().peekable();
4719 let mut ranges_to_highlight = Vec::new();
4720
4721 let excerpt_buffer = cx.add_model(|cx| {
4722 let mut symbol_name = None;
4723 let mut multibuffer = MultiBuffer::new(replica_id);
4724 while let Some(location) = locations.next() {
4725 let buffer = location.buffer.read(cx);
4726 let mut ranges_for_buffer = Vec::new();
4727 let range = location.range.to_offset(buffer);
4728 ranges_for_buffer.push(range.clone());
4729 if symbol_name.is_none() {
4730 symbol_name = Some(buffer.text_for_range(range).collect::<String>());
4731 }
4732
4733 while let Some(next_location) = locations.peek() {
4734 if next_location.buffer == location.buffer {
4735 ranges_for_buffer.push(next_location.range.to_offset(buffer));
4736 locations.next();
4737 } else {
4738 break;
4739 }
4740 }
4741
4742 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
4743 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
4744 location.buffer.clone(),
4745 ranges_for_buffer,
4746 1,
4747 cx,
4748 ));
4749 }
4750 multibuffer.with_title(format!("References to `{}`", symbol_name.unwrap()))
4751 });
4752
4753 workspace.update(&mut cx, |workspace, cx| {
4754 let editor =
4755 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
4756 editor.update(cx, |editor, cx| {
4757 editor.highlight_background::<Self>(
4758 ranges_to_highlight,
4759 |theme| theme.editor.highlighted_line_background,
4760 cx,
4761 );
4762 });
4763 workspace.add_item(Box::new(editor), cx);
4764 });
4765
4766 Ok(())
4767 }))
4768 }
4769
4770 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
4771 use language::ToOffset as _;
4772
4773 let project = self.project.clone()?;
4774 let selection = self.selections.newest_anchor().clone();
4775 let (cursor_buffer, cursor_buffer_position) = self
4776 .buffer
4777 .read(cx)
4778 .text_anchor_for_position(selection.head(), cx)?;
4779 let (tail_buffer, _) = self
4780 .buffer
4781 .read(cx)
4782 .text_anchor_for_position(selection.tail(), cx)?;
4783 if tail_buffer != cursor_buffer {
4784 return None;
4785 }
4786
4787 let snapshot = cursor_buffer.read(cx).snapshot();
4788 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
4789 let prepare_rename = project.update(cx, |project, cx| {
4790 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
4791 });
4792
4793 Some(cx.spawn(|this, mut cx| async move {
4794 let rename_range = if let Some(range) = prepare_rename.await? {
4795 Some(range)
4796 } else {
4797 this.read_with(&cx, |this, cx| {
4798 let buffer = this.buffer.read(cx).snapshot(cx);
4799 let mut buffer_highlights = this
4800 .document_highlights_for_position(selection.head(), &buffer)
4801 .filter(|highlight| {
4802 highlight.start.excerpt_id() == selection.head().excerpt_id()
4803 && highlight.end.excerpt_id() == selection.head().excerpt_id()
4804 });
4805 buffer_highlights
4806 .next()
4807 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
4808 })
4809 };
4810 if let Some(rename_range) = rename_range {
4811 let rename_buffer_range = rename_range.to_offset(&snapshot);
4812 let cursor_offset_in_rename_range =
4813 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
4814
4815 this.update(&mut cx, |this, cx| {
4816 this.take_rename(false, cx);
4817 let style = this.style(cx);
4818 let buffer = this.buffer.read(cx).read(cx);
4819 let cursor_offset = selection.head().to_offset(&buffer);
4820 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
4821 let rename_end = rename_start + rename_buffer_range.len();
4822 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
4823 let mut old_highlight_id = None;
4824 let old_name: Arc<str> = buffer
4825 .chunks(rename_start..rename_end, true)
4826 .map(|chunk| {
4827 if old_highlight_id.is_none() {
4828 old_highlight_id = chunk.syntax_highlight_id;
4829 }
4830 chunk.text
4831 })
4832 .collect::<String>()
4833 .into();
4834
4835 drop(buffer);
4836
4837 // Position the selection in the rename editor so that it matches the current selection.
4838 this.show_local_selections = false;
4839 let rename_editor = cx.add_view(|cx| {
4840 let mut editor = Editor::single_line(None, cx);
4841 if let Some(old_highlight_id) = old_highlight_id {
4842 editor.override_text_style =
4843 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
4844 }
4845 editor
4846 .buffer
4847 .update(cx, |buffer, cx| buffer.edit([(0..0, old_name.clone())], cx));
4848 editor.select_all(&SelectAll, cx);
4849 editor
4850 });
4851
4852 let ranges = this
4853 .clear_background_highlights::<DocumentHighlightWrite>(cx)
4854 .into_iter()
4855 .flat_map(|(_, ranges)| ranges)
4856 .chain(
4857 this.clear_background_highlights::<DocumentHighlightRead>(cx)
4858 .into_iter()
4859 .flat_map(|(_, ranges)| ranges),
4860 )
4861 .collect();
4862
4863 this.highlight_text::<Rename>(
4864 ranges,
4865 HighlightStyle {
4866 fade_out: Some(style.rename_fade),
4867 ..Default::default()
4868 },
4869 cx,
4870 );
4871 cx.focus(&rename_editor);
4872 let block_id = this.insert_blocks(
4873 [BlockProperties {
4874 style: BlockStyle::Flex,
4875 position: range.start.clone(),
4876 height: 1,
4877 render: Arc::new({
4878 let editor = rename_editor.clone();
4879 move |cx: &mut BlockContext| {
4880 ChildView::new(editor.clone())
4881 .contained()
4882 .with_padding_left(cx.anchor_x)
4883 .boxed()
4884 }
4885 }),
4886 disposition: BlockDisposition::Below,
4887 }],
4888 cx,
4889 )[0];
4890 this.pending_rename = Some(RenameState {
4891 range,
4892 old_name,
4893 editor: rename_editor,
4894 block_id,
4895 });
4896 });
4897 }
4898
4899 Ok(())
4900 }))
4901 }
4902
4903 pub fn confirm_rename(
4904 workspace: &mut Workspace,
4905 _: &ConfirmRename,
4906 cx: &mut ViewContext<Workspace>,
4907 ) -> Option<Task<Result<()>>> {
4908 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
4909
4910 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
4911 let rename = editor.take_rename(false, cx)?;
4912 let buffer = editor.buffer.read(cx);
4913 let (start_buffer, start) =
4914 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
4915 let (end_buffer, end) =
4916 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
4917 if start_buffer == end_buffer {
4918 let new_name = rename.editor.read(cx).text(cx);
4919 Some((start_buffer, start..end, rename.old_name, new_name))
4920 } else {
4921 None
4922 }
4923 })?;
4924
4925 let rename = workspace.project().clone().update(cx, |project, cx| {
4926 project.perform_rename(
4927 buffer.clone(),
4928 range.start.clone(),
4929 new_name.clone(),
4930 true,
4931 cx,
4932 )
4933 });
4934
4935 Some(cx.spawn(|workspace, mut cx| async move {
4936 let project_transaction = rename.await?;
4937 Self::open_project_transaction(
4938 editor.clone(),
4939 workspace,
4940 project_transaction,
4941 format!("Rename: {} → {}", old_name, new_name),
4942 cx.clone(),
4943 )
4944 .await?;
4945
4946 editor.update(&mut cx, |editor, cx| {
4947 editor.refresh_document_highlights(cx);
4948 });
4949 Ok(())
4950 }))
4951 }
4952
4953 fn take_rename(
4954 &mut self,
4955 moving_cursor: bool,
4956 cx: &mut ViewContext<Self>,
4957 ) -> Option<RenameState> {
4958 let rename = self.pending_rename.take()?;
4959 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
4960 self.clear_text_highlights::<Rename>(cx);
4961 self.show_local_selections = true;
4962
4963 if moving_cursor {
4964 let rename_editor = rename.editor.read(cx);
4965 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
4966
4967 // Update the selection to match the position of the selection inside
4968 // the rename editor.
4969 let snapshot = self.buffer.read(cx).read(cx);
4970 let rename_range = rename.range.to_offset(&snapshot);
4971 let cursor_in_editor = snapshot
4972 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
4973 .min(rename_range.end);
4974 drop(snapshot);
4975
4976 self.change_selections(None, cx, |s| {
4977 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
4978 });
4979 }
4980
4981 Some(rename)
4982 }
4983
4984 #[cfg(any(test, feature = "test-support"))]
4985 pub fn pending_rename(&self) -> Option<&RenameState> {
4986 self.pending_rename.as_ref()
4987 }
4988
4989 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
4990 if let Some(project) = self.project.clone() {
4991 self.buffer.update(cx, |multi_buffer, cx| {
4992 project.update(cx, |project, cx| {
4993 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
4994 });
4995 })
4996 }
4997 }
4998
4999 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
5000 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
5001 let buffer = self.buffer.read(cx).snapshot(cx);
5002 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
5003 let is_valid = buffer
5004 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
5005 .any(|entry| {
5006 entry.diagnostic.is_primary
5007 && !entry.range.is_empty()
5008 && entry.range.start == primary_range_start
5009 && entry.diagnostic.message == active_diagnostics.primary_message
5010 });
5011
5012 if is_valid != active_diagnostics.is_valid {
5013 active_diagnostics.is_valid = is_valid;
5014 let mut new_styles = HashMap::default();
5015 for (block_id, diagnostic) in &active_diagnostics.blocks {
5016 new_styles.insert(
5017 *block_id,
5018 diagnostic_block_renderer(diagnostic.clone(), is_valid),
5019 );
5020 }
5021 self.display_map
5022 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
5023 }
5024 }
5025 }
5026
5027 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) {
5028 self.dismiss_diagnostics(cx);
5029 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
5030 let buffer = self.buffer.read(cx).snapshot(cx);
5031
5032 let mut primary_range = None;
5033 let mut primary_message = None;
5034 let mut group_end = Point::zero();
5035 let diagnostic_group = buffer
5036 .diagnostic_group::<Point>(group_id)
5037 .map(|entry| {
5038 if entry.range.end > group_end {
5039 group_end = entry.range.end;
5040 }
5041 if entry.diagnostic.is_primary {
5042 primary_range = Some(entry.range.clone());
5043 primary_message = Some(entry.diagnostic.message.clone());
5044 }
5045 entry
5046 })
5047 .collect::<Vec<_>>();
5048 let primary_range = primary_range.unwrap();
5049 let primary_message = primary_message.unwrap();
5050 let primary_range =
5051 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
5052
5053 let blocks = display_map
5054 .insert_blocks(
5055 diagnostic_group.iter().map(|entry| {
5056 let diagnostic = entry.diagnostic.clone();
5057 let message_height = diagnostic.message.lines().count() as u8;
5058 BlockProperties {
5059 style: BlockStyle::Fixed,
5060 position: buffer.anchor_after(entry.range.start),
5061 height: message_height,
5062 render: diagnostic_block_renderer(diagnostic, true),
5063 disposition: BlockDisposition::Below,
5064 }
5065 }),
5066 cx,
5067 )
5068 .into_iter()
5069 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
5070 .collect();
5071
5072 Some(ActiveDiagnosticGroup {
5073 primary_range,
5074 primary_message,
5075 blocks,
5076 is_valid: true,
5077 })
5078 });
5079 }
5080
5081 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
5082 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
5083 self.display_map.update(cx, |display_map, cx| {
5084 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
5085 });
5086 cx.notify();
5087 }
5088 }
5089
5090 pub fn set_selections_from_remote(
5091 &mut self,
5092 selections: Vec<Selection<Anchor>>,
5093 cx: &mut ViewContext<Self>,
5094 ) {
5095 let old_cursor_position = self.selections.newest_anchor().head();
5096 self.selections.change_with(cx, |s| {
5097 s.select_anchors(selections);
5098 });
5099 self.selections_did_change(false, &old_cursor_position, cx);
5100 }
5101
5102 fn push_to_selection_history(&mut self) {
5103 self.selection_history.push(SelectionHistoryEntry {
5104 selections: self.selections.disjoint_anchors().clone(),
5105 select_next_state: self.select_next_state.clone(),
5106 add_selections_state: self.add_selections_state.clone(),
5107 });
5108 }
5109
5110 pub fn request_autoscroll(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5111 self.autoscroll_request = Some((autoscroll, true));
5112 cx.notify();
5113 }
5114
5115 fn request_autoscroll_remotely(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5116 self.autoscroll_request = Some((autoscroll, false));
5117 cx.notify();
5118 }
5119
5120 pub fn transact(
5121 &mut self,
5122 cx: &mut ViewContext<Self>,
5123 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
5124 ) {
5125 self.start_transaction_at(Instant::now(), cx);
5126 update(self, cx);
5127 self.end_transaction_at(Instant::now(), cx);
5128 }
5129
5130 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
5131 self.end_selection(cx);
5132 if let Some(tx_id) = self
5133 .buffer
5134 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
5135 {
5136 self.selection_history
5137 .insert_transaction(tx_id, self.selections.disjoint_anchors().clone());
5138 }
5139 }
5140
5141 fn end_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
5142 if let Some(tx_id) = self
5143 .buffer
5144 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
5145 {
5146 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
5147 *end_selections = Some(self.selections.disjoint_anchors().clone());
5148 } else {
5149 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
5150 }
5151
5152 cx.emit(Event::Edited);
5153 }
5154 }
5155
5156 pub fn page_up(&mut self, _: &PageUp, _: &mut ViewContext<Self>) {
5157 log::info!("Editor::page_up");
5158 }
5159
5160 pub fn page_down(&mut self, _: &PageDown, _: &mut ViewContext<Self>) {
5161 log::info!("Editor::page_down");
5162 }
5163
5164 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
5165 let mut fold_ranges = Vec::new();
5166
5167 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5168 let selections = self.selections.all::<Point>(cx);
5169 for selection in selections {
5170 let range = selection.display_range(&display_map).sorted();
5171 let buffer_start_row = range.start.to_point(&display_map).row;
5172
5173 for row in (0..=range.end.row()).rev() {
5174 if self.is_line_foldable(&display_map, row) && !display_map.is_line_folded(row) {
5175 let fold_range = self.foldable_range_for_line(&display_map, row);
5176 if fold_range.end.row >= buffer_start_row {
5177 fold_ranges.push(fold_range);
5178 if row <= range.start.row() {
5179 break;
5180 }
5181 }
5182 }
5183 }
5184 }
5185
5186 self.fold_ranges(fold_ranges, cx);
5187 }
5188
5189 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
5190 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5191 let buffer = &display_map.buffer_snapshot;
5192 let selections = self.selections.all::<Point>(cx);
5193 let ranges = selections
5194 .iter()
5195 .map(|s| {
5196 let range = s.display_range(&display_map).sorted();
5197 let mut start = range.start.to_point(&display_map);
5198 let mut end = range.end.to_point(&display_map);
5199 start.column = 0;
5200 end.column = buffer.line_len(end.row);
5201 start..end
5202 })
5203 .collect::<Vec<_>>();
5204 self.unfold_ranges(ranges, true, cx);
5205 }
5206
5207 fn is_line_foldable(&self, display_map: &DisplaySnapshot, display_row: u32) -> bool {
5208 let max_point = display_map.max_point();
5209 if display_row >= max_point.row() {
5210 false
5211 } else {
5212 let (start_indent, is_blank) = display_map.line_indent(display_row);
5213 if is_blank {
5214 false
5215 } else {
5216 for display_row in display_row + 1..=max_point.row() {
5217 let (indent, is_blank) = display_map.line_indent(display_row);
5218 if !is_blank {
5219 return indent > start_indent;
5220 }
5221 }
5222 false
5223 }
5224 }
5225 }
5226
5227 fn foldable_range_for_line(
5228 &self,
5229 display_map: &DisplaySnapshot,
5230 start_row: u32,
5231 ) -> Range<Point> {
5232 let max_point = display_map.max_point();
5233
5234 let (start_indent, _) = display_map.line_indent(start_row);
5235 let start = DisplayPoint::new(start_row, display_map.line_len(start_row));
5236 let mut end = None;
5237 for row in start_row + 1..=max_point.row() {
5238 let (indent, is_blank) = display_map.line_indent(row);
5239 if !is_blank && indent <= start_indent {
5240 end = Some(DisplayPoint::new(row - 1, display_map.line_len(row - 1)));
5241 break;
5242 }
5243 }
5244
5245 let end = end.unwrap_or(max_point);
5246 return start.to_point(display_map)..end.to_point(display_map);
5247 }
5248
5249 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
5250 let selections = self.selections.all::<Point>(cx);
5251 let ranges = selections.into_iter().map(|s| s.start..s.end);
5252 self.fold_ranges(ranges, cx);
5253 }
5254
5255 pub fn fold_ranges<T: ToOffset>(
5256 &mut self,
5257 ranges: impl IntoIterator<Item = Range<T>>,
5258 cx: &mut ViewContext<Self>,
5259 ) {
5260 let mut ranges = ranges.into_iter().peekable();
5261 if ranges.peek().is_some() {
5262 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
5263 self.request_autoscroll(Autoscroll::Fit, cx);
5264 cx.notify();
5265 }
5266 }
5267
5268 pub fn unfold_ranges<T: ToOffset>(
5269 &mut self,
5270 ranges: impl IntoIterator<Item = Range<T>>,
5271 inclusive: bool,
5272 cx: &mut ViewContext<Self>,
5273 ) {
5274 let mut ranges = ranges.into_iter().peekable();
5275 if ranges.peek().is_some() {
5276 self.display_map
5277 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
5278 self.request_autoscroll(Autoscroll::Fit, cx);
5279 cx.notify();
5280 }
5281 }
5282
5283 pub fn insert_blocks(
5284 &mut self,
5285 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
5286 cx: &mut ViewContext<Self>,
5287 ) -> Vec<BlockId> {
5288 let blocks = self
5289 .display_map
5290 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
5291 self.request_autoscroll(Autoscroll::Fit, cx);
5292 blocks
5293 }
5294
5295 pub fn replace_blocks(
5296 &mut self,
5297 blocks: HashMap<BlockId, RenderBlock>,
5298 cx: &mut ViewContext<Self>,
5299 ) {
5300 self.display_map
5301 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
5302 self.request_autoscroll(Autoscroll::Fit, cx);
5303 }
5304
5305 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
5306 self.display_map.update(cx, |display_map, cx| {
5307 display_map.remove_blocks(block_ids, cx)
5308 });
5309 }
5310
5311 pub fn longest_row(&self, cx: &mut MutableAppContext) -> u32 {
5312 self.display_map
5313 .update(cx, |map, cx| map.snapshot(cx))
5314 .longest_row()
5315 }
5316
5317 pub fn max_point(&self, cx: &mut MutableAppContext) -> DisplayPoint {
5318 self.display_map
5319 .update(cx, |map, cx| map.snapshot(cx))
5320 .max_point()
5321 }
5322
5323 pub fn text(&self, cx: &AppContext) -> String {
5324 self.buffer.read(cx).read(cx).text()
5325 }
5326
5327 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
5328 self.transact(cx, |this, cx| {
5329 this.buffer
5330 .read(cx)
5331 .as_singleton()
5332 .expect("you can only call set_text on editors for singleton buffers")
5333 .update(cx, |buffer, cx| buffer.set_text(text, cx));
5334 });
5335 }
5336
5337 pub fn display_text(&self, cx: &mut MutableAppContext) -> String {
5338 self.display_map
5339 .update(cx, |map, cx| map.snapshot(cx))
5340 .text()
5341 }
5342
5343 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
5344 let language_name = self
5345 .buffer
5346 .read(cx)
5347 .as_singleton()
5348 .and_then(|singleton_buffer| singleton_buffer.read(cx).language())
5349 .map(|l| l.name());
5350
5351 let settings = cx.global::<Settings>();
5352 let mode = self
5353 .soft_wrap_mode_override
5354 .unwrap_or_else(|| settings.soft_wrap(language_name.as_deref()));
5355 match mode {
5356 settings::SoftWrap::None => SoftWrap::None,
5357 settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
5358 settings::SoftWrap::PreferredLineLength => {
5359 SoftWrap::Column(settings.preferred_line_length(language_name.as_deref()))
5360 }
5361 }
5362 }
5363
5364 pub fn set_soft_wrap_mode(&mut self, mode: settings::SoftWrap, cx: &mut ViewContext<Self>) {
5365 self.soft_wrap_mode_override = Some(mode);
5366 cx.notify();
5367 }
5368
5369 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut MutableAppContext) -> bool {
5370 self.display_map
5371 .update(cx, |map, cx| map.set_wrap_width(width, cx))
5372 }
5373
5374 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
5375 self.highlighted_rows = rows;
5376 }
5377
5378 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
5379 self.highlighted_rows.clone()
5380 }
5381
5382 pub fn highlight_background<T: 'static>(
5383 &mut self,
5384 ranges: Vec<Range<Anchor>>,
5385 color_fetcher: fn(&Theme) -> Color,
5386 cx: &mut ViewContext<Self>,
5387 ) {
5388 self.background_highlights
5389 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
5390 cx.notify();
5391 }
5392
5393 pub fn clear_background_highlights<T: 'static>(
5394 &mut self,
5395 cx: &mut ViewContext<Self>,
5396 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
5397 cx.notify();
5398 self.background_highlights.remove(&TypeId::of::<T>())
5399 }
5400
5401 #[cfg(feature = "test-support")]
5402 pub fn all_background_highlights(
5403 &mut self,
5404 cx: &mut ViewContext<Self>,
5405 ) -> Vec<(Range<DisplayPoint>, Color)> {
5406 let snapshot = self.snapshot(cx);
5407 let buffer = &snapshot.buffer_snapshot;
5408 let start = buffer.anchor_before(0);
5409 let end = buffer.anchor_after(buffer.len());
5410 let theme = cx.global::<Settings>().theme.as_ref();
5411 self.background_highlights_in_range(start..end, &snapshot, theme)
5412 }
5413
5414 fn document_highlights_for_position<'a>(
5415 &'a self,
5416 position: Anchor,
5417 buffer: &'a MultiBufferSnapshot,
5418 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
5419 let read_highlights = self
5420 .background_highlights
5421 .get(&TypeId::of::<DocumentHighlightRead>())
5422 .map(|h| &h.1);
5423 let write_highlights = self
5424 .background_highlights
5425 .get(&TypeId::of::<DocumentHighlightWrite>())
5426 .map(|h| &h.1);
5427 let left_position = position.bias_left(buffer);
5428 let right_position = position.bias_right(buffer);
5429 read_highlights
5430 .into_iter()
5431 .chain(write_highlights)
5432 .flat_map(move |ranges| {
5433 let start_ix = match ranges.binary_search_by(|probe| {
5434 let cmp = probe.end.cmp(&left_position, &buffer);
5435 if cmp.is_ge() {
5436 Ordering::Greater
5437 } else {
5438 Ordering::Less
5439 }
5440 }) {
5441 Ok(i) | Err(i) => i,
5442 };
5443
5444 let right_position = right_position.clone();
5445 ranges[start_ix..]
5446 .iter()
5447 .take_while(move |range| range.start.cmp(&right_position, &buffer).is_le())
5448 })
5449 }
5450
5451 pub fn background_highlights_in_range(
5452 &self,
5453 search_range: Range<Anchor>,
5454 display_snapshot: &DisplaySnapshot,
5455 theme: &Theme,
5456 ) -> Vec<(Range<DisplayPoint>, Color)> {
5457 let mut results = Vec::new();
5458 let buffer = &display_snapshot.buffer_snapshot;
5459 for (color_fetcher, ranges) in self.background_highlights.values() {
5460 let color = color_fetcher(theme);
5461 let start_ix = match ranges.binary_search_by(|probe| {
5462 let cmp = probe.end.cmp(&search_range.start, &buffer);
5463 if cmp.is_gt() {
5464 Ordering::Greater
5465 } else {
5466 Ordering::Less
5467 }
5468 }) {
5469 Ok(i) | Err(i) => i,
5470 };
5471 for range in &ranges[start_ix..] {
5472 if range.start.cmp(&search_range.end, &buffer).is_ge() {
5473 break;
5474 }
5475 let start = range
5476 .start
5477 .to_point(buffer)
5478 .to_display_point(display_snapshot);
5479 let end = range
5480 .end
5481 .to_point(buffer)
5482 .to_display_point(display_snapshot);
5483 results.push((start..end, color))
5484 }
5485 }
5486 results
5487 }
5488
5489 pub fn highlight_text<T: 'static>(
5490 &mut self,
5491 ranges: Vec<Range<Anchor>>,
5492 style: HighlightStyle,
5493 cx: &mut ViewContext<Self>,
5494 ) {
5495 self.display_map.update(cx, |map, _| {
5496 map.highlight_text(TypeId::of::<T>(), ranges, style)
5497 });
5498 cx.notify();
5499 }
5500
5501 pub fn clear_text_highlights<T: 'static>(
5502 &mut self,
5503 cx: &mut ViewContext<Self>,
5504 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
5505 cx.notify();
5506 self.display_map
5507 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()))
5508 }
5509
5510 fn next_blink_epoch(&mut self) -> usize {
5511 self.blink_epoch += 1;
5512 self.blink_epoch
5513 }
5514
5515 fn pause_cursor_blinking(&mut self, cx: &mut ViewContext<Self>) {
5516 if !self.focused {
5517 return;
5518 }
5519
5520 self.show_local_cursors = true;
5521 cx.notify();
5522
5523 let epoch = self.next_blink_epoch();
5524 cx.spawn(|this, mut cx| {
5525 let this = this.downgrade();
5526 async move {
5527 Timer::after(CURSOR_BLINK_INTERVAL).await;
5528 if let Some(this) = this.upgrade(&cx) {
5529 this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
5530 }
5531 }
5532 })
5533 .detach();
5534 }
5535
5536 fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5537 if epoch == self.blink_epoch {
5538 self.blinking_paused = false;
5539 self.blink_cursors(epoch, cx);
5540 }
5541 }
5542
5543 fn blink_cursors(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5544 if epoch == self.blink_epoch && self.focused && !self.blinking_paused {
5545 self.show_local_cursors = !self.show_local_cursors;
5546 cx.notify();
5547
5548 let epoch = self.next_blink_epoch();
5549 cx.spawn(|this, mut cx| {
5550 let this = this.downgrade();
5551 async move {
5552 Timer::after(CURSOR_BLINK_INTERVAL).await;
5553 if let Some(this) = this.upgrade(&cx) {
5554 this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx));
5555 }
5556 }
5557 })
5558 .detach();
5559 }
5560 }
5561
5562 pub fn show_local_cursors(&self) -> bool {
5563 self.show_local_cursors && self.focused
5564 }
5565
5566 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
5567 cx.notify();
5568 }
5569
5570 fn on_buffer_event(
5571 &mut self,
5572 _: ModelHandle<MultiBuffer>,
5573 event: &language::Event,
5574 cx: &mut ViewContext<Self>,
5575 ) {
5576 match event {
5577 language::Event::Edited => {
5578 self.refresh_active_diagnostics(cx);
5579 self.refresh_code_actions(cx);
5580 cx.emit(Event::BufferEdited);
5581 }
5582 language::Event::Reparsed => cx.emit(Event::Reparsed),
5583 language::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
5584 language::Event::Saved => cx.emit(Event::Saved),
5585 language::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
5586 language::Event::Reloaded => cx.emit(Event::TitleChanged),
5587 language::Event::Closed => cx.emit(Event::Closed),
5588 language::Event::DiagnosticsUpdated => {
5589 self.refresh_active_diagnostics(cx);
5590 }
5591 _ => {}
5592 }
5593 }
5594
5595 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
5596 cx.notify();
5597 }
5598
5599 pub fn set_searchable(&mut self, searchable: bool) {
5600 self.searchable = searchable;
5601 }
5602
5603 pub fn searchable(&self) -> bool {
5604 self.searchable
5605 }
5606
5607 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
5608 let active_item = workspace.active_item(cx);
5609 let editor_handle = if let Some(editor) = active_item
5610 .as_ref()
5611 .and_then(|item| item.act_as::<Self>(cx))
5612 {
5613 editor
5614 } else {
5615 cx.propagate_action();
5616 return;
5617 };
5618
5619 let editor = editor_handle.read(cx);
5620 let buffer = editor.buffer.read(cx);
5621 if buffer.is_singleton() {
5622 cx.propagate_action();
5623 return;
5624 }
5625
5626 let mut new_selections_by_buffer = HashMap::default();
5627 for selection in editor.selections.all::<usize>(cx) {
5628 for (buffer, mut range) in
5629 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
5630 {
5631 if selection.reversed {
5632 mem::swap(&mut range.start, &mut range.end);
5633 }
5634 new_selections_by_buffer
5635 .entry(buffer)
5636 .or_insert(Vec::new())
5637 .push(range)
5638 }
5639 }
5640
5641 editor_handle.update(cx, |editor, cx| {
5642 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
5643 });
5644 let nav_history = workspace.active_pane().read(cx).nav_history().clone();
5645 nav_history.borrow_mut().disable();
5646
5647 // We defer the pane interaction because we ourselves are a workspace item
5648 // and activating a new item causes the pane to call a method on us reentrantly,
5649 // which panics if we're on the stack.
5650 cx.defer(move |workspace, cx| {
5651 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
5652 let editor = workspace.open_project_item::<Self>(buffer, cx);
5653 editor.update(cx, |editor, cx| {
5654 editor.change_selections(Some(Autoscroll::Newest), cx, |s| {
5655 s.select_ranges(ranges);
5656 });
5657 });
5658 }
5659
5660 nav_history.borrow_mut().enable();
5661 });
5662 }
5663
5664 fn jump(workspace: &mut Workspace, action: &Jump, cx: &mut ViewContext<Workspace>) {
5665 let editor = workspace.open_path(action.path.clone(), true, cx);
5666 let position = action.position;
5667 let anchor = action.anchor;
5668 cx.spawn_weak(|_, mut cx| async move {
5669 let editor = editor.await.log_err()?.downcast::<Editor>()?;
5670 editor.update(&mut cx, |editor, cx| {
5671 let buffer = editor.buffer().read(cx).as_singleton()?;
5672 let buffer = buffer.read(cx);
5673 let cursor = if buffer.can_resolve(&anchor) {
5674 language::ToPoint::to_point(&anchor, buffer)
5675 } else {
5676 buffer.clip_point(position, Bias::Left)
5677 };
5678
5679 let nav_history = editor.nav_history.take();
5680 editor.change_selections(Some(Autoscroll::Newest), cx, |s| {
5681 s.select_ranges([cursor..cursor]);
5682 });
5683 editor.nav_history = nav_history;
5684
5685 Some(())
5686 })?;
5687 Some(())
5688 })
5689 .detach()
5690 }
5691}
5692
5693impl EditorSnapshot {
5694 pub fn is_focused(&self) -> bool {
5695 self.is_focused
5696 }
5697
5698 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
5699 self.placeholder_text.as_ref()
5700 }
5701
5702 pub fn scroll_position(&self) -> Vector2F {
5703 compute_scroll_position(
5704 &self.display_snapshot,
5705 self.scroll_position,
5706 &self.scroll_top_anchor,
5707 )
5708 }
5709}
5710
5711impl Deref for EditorSnapshot {
5712 type Target = DisplaySnapshot;
5713
5714 fn deref(&self) -> &Self::Target {
5715 &self.display_snapshot
5716 }
5717}
5718
5719fn compute_scroll_position(
5720 snapshot: &DisplaySnapshot,
5721 mut scroll_position: Vector2F,
5722 scroll_top_anchor: &Anchor,
5723) -> Vector2F {
5724 if *scroll_top_anchor != Anchor::min() {
5725 let scroll_top = scroll_top_anchor.to_display_point(snapshot).row() as f32;
5726 scroll_position.set_y(scroll_top + scroll_position.y());
5727 } else {
5728 scroll_position.set_y(0.);
5729 }
5730 scroll_position
5731}
5732
5733#[derive(Copy, Clone, Debug, PartialEq, Eq)]
5734pub enum Event {
5735 Activate,
5736 BufferEdited,
5737 Edited,
5738 Reparsed,
5739 Blurred,
5740 DirtyChanged,
5741 Saved,
5742 TitleChanged,
5743 SelectionsChanged { local: bool },
5744 ScrollPositionChanged { local: bool },
5745 Closed,
5746}
5747
5748pub struct EditorFocused(pub ViewHandle<Editor>);
5749pub struct EditorBlurred(pub ViewHandle<Editor>);
5750pub struct EditorReleased(pub WeakViewHandle<Editor>);
5751
5752impl Entity for Editor {
5753 type Event = Event;
5754
5755 fn release(&mut self, cx: &mut MutableAppContext) {
5756 cx.emit_global(EditorReleased(self.handle.clone()));
5757 }
5758}
5759
5760impl View for Editor {
5761 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
5762 let style = self.style(cx);
5763 let font_changed = self.display_map.update(cx, |map, cx| {
5764 map.set_font(style.text.font_id, style.text.font_size, cx)
5765 });
5766
5767 if font_changed {
5768 let handle = self.handle.clone();
5769 cx.defer(move |cx| {
5770 if let Some(editor) = handle.upgrade(cx) {
5771 editor.update(cx, |editor, cx| {
5772 hide_hover(editor, cx);
5773 })
5774 }
5775 });
5776 }
5777
5778 EditorElement::new(self.handle.clone(), style.clone(), self.cursor_shape).boxed()
5779 }
5780
5781 fn ui_name() -> &'static str {
5782 "Editor"
5783 }
5784
5785 fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
5786 let focused_event = EditorFocused(cx.handle());
5787 cx.emit_global(focused_event);
5788 if let Some(rename) = self.pending_rename.as_ref() {
5789 cx.focus(&rename.editor);
5790 } else {
5791 self.focused = true;
5792 self.blink_cursors(self.blink_epoch, cx);
5793 self.buffer.update(cx, |buffer, cx| {
5794 buffer.finalize_last_transaction(cx);
5795 if self.leader_replica_id.is_none() {
5796 buffer.set_active_selections(
5797 &self.selections.disjoint_anchors(),
5798 self.selections.line_mode,
5799 cx,
5800 );
5801 }
5802 });
5803 }
5804 }
5805
5806 fn on_blur(&mut self, cx: &mut ViewContext<Self>) {
5807 let blurred_event = EditorBlurred(cx.handle());
5808 cx.emit_global(blurred_event);
5809 self.focused = false;
5810 self.buffer
5811 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
5812 self.hide_context_menu(cx);
5813 hide_hover(self, cx);
5814 cx.emit(Event::Blurred);
5815 cx.notify();
5816 }
5817
5818 fn keymap_context(&self, _: &AppContext) -> gpui::keymap::Context {
5819 let mut context = Self::default_keymap_context();
5820 let mode = match self.mode {
5821 EditorMode::SingleLine => "single_line",
5822 EditorMode::AutoHeight { .. } => "auto_height",
5823 EditorMode::Full => "full",
5824 };
5825 context.map.insert("mode".into(), mode.into());
5826 if self.pending_rename.is_some() {
5827 context.set.insert("renaming".into());
5828 }
5829 match self.context_menu.as_ref() {
5830 Some(ContextMenu::Completions(_)) => {
5831 context.set.insert("showing_completions".into());
5832 }
5833 Some(ContextMenu::CodeActions(_)) => {
5834 context.set.insert("showing_code_actions".into());
5835 }
5836 None => {}
5837 }
5838
5839 for layer in self.keymap_context_layers.values() {
5840 context.extend(layer);
5841 }
5842
5843 context
5844 }
5845}
5846
5847fn build_style(
5848 settings: &Settings,
5849 get_field_editor_theme: Option<GetFieldEditorTheme>,
5850 override_text_style: Option<&OverrideTextStyle>,
5851 cx: &AppContext,
5852) -> EditorStyle {
5853 let font_cache = cx.font_cache();
5854
5855 let mut theme = settings.theme.editor.clone();
5856 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
5857 let field_editor_theme = get_field_editor_theme(&settings.theme);
5858 theme.text_color = field_editor_theme.text.color;
5859 theme.selection = field_editor_theme.selection;
5860 theme.background = field_editor_theme
5861 .container
5862 .background_color
5863 .unwrap_or_default();
5864 EditorStyle {
5865 text: field_editor_theme.text,
5866 placeholder_text: field_editor_theme.placeholder_text,
5867 theme,
5868 }
5869 } else {
5870 let font_family_id = settings.buffer_font_family;
5871 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
5872 let font_properties = Default::default();
5873 let font_id = font_cache
5874 .select_font(font_family_id, &font_properties)
5875 .unwrap();
5876 let font_size = settings.buffer_font_size;
5877 EditorStyle {
5878 text: TextStyle {
5879 color: settings.theme.editor.text_color,
5880 font_family_name,
5881 font_family_id,
5882 font_id,
5883 font_size,
5884 font_properties,
5885 underline: Default::default(),
5886 },
5887 placeholder_text: None,
5888 theme,
5889 }
5890 };
5891
5892 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
5893 if let Some(highlighted) = style
5894 .text
5895 .clone()
5896 .highlight(highlight_style, font_cache)
5897 .log_err()
5898 {
5899 style.text = highlighted;
5900 }
5901 }
5902
5903 style
5904}
5905
5906trait SelectionExt {
5907 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
5908 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
5909 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
5910 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
5911 -> Range<u32>;
5912}
5913
5914impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
5915 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
5916 let start = self.start.to_point(buffer);
5917 let end = self.end.to_point(buffer);
5918 if self.reversed {
5919 end..start
5920 } else {
5921 start..end
5922 }
5923 }
5924
5925 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
5926 let start = self.start.to_offset(buffer);
5927 let end = self.end.to_offset(buffer);
5928 if self.reversed {
5929 end..start
5930 } else {
5931 start..end
5932 }
5933 }
5934
5935 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
5936 let start = self
5937 .start
5938 .to_point(&map.buffer_snapshot)
5939 .to_display_point(map);
5940 let end = self
5941 .end
5942 .to_point(&map.buffer_snapshot)
5943 .to_display_point(map);
5944 if self.reversed {
5945 end..start
5946 } else {
5947 start..end
5948 }
5949 }
5950
5951 fn spanned_rows(
5952 &self,
5953 include_end_if_at_line_start: bool,
5954 map: &DisplaySnapshot,
5955 ) -> Range<u32> {
5956 let start = self.start.to_point(&map.buffer_snapshot);
5957 let mut end = self.end.to_point(&map.buffer_snapshot);
5958 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
5959 end.row -= 1;
5960 }
5961
5962 let buffer_start = map.prev_line_boundary(start).0;
5963 let buffer_end = map.next_line_boundary(end).0;
5964 buffer_start.row..buffer_end.row + 1
5965 }
5966}
5967
5968impl<T: InvalidationRegion> InvalidationStack<T> {
5969 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
5970 where
5971 S: Clone + ToOffset,
5972 {
5973 while let Some(region) = self.last() {
5974 let all_selections_inside_invalidation_ranges =
5975 if selections.len() == region.ranges().len() {
5976 selections
5977 .iter()
5978 .zip(region.ranges().iter().map(|r| r.to_offset(&buffer)))
5979 .all(|(selection, invalidation_range)| {
5980 let head = selection.head().to_offset(&buffer);
5981 invalidation_range.start <= head && invalidation_range.end >= head
5982 })
5983 } else {
5984 false
5985 };
5986
5987 if all_selections_inside_invalidation_ranges {
5988 break;
5989 } else {
5990 self.pop();
5991 }
5992 }
5993 }
5994}
5995
5996impl<T> Default for InvalidationStack<T> {
5997 fn default() -> Self {
5998 Self(Default::default())
5999 }
6000}
6001
6002impl<T> Deref for InvalidationStack<T> {
6003 type Target = Vec<T>;
6004
6005 fn deref(&self) -> &Self::Target {
6006 &self.0
6007 }
6008}
6009
6010impl<T> DerefMut for InvalidationStack<T> {
6011 fn deref_mut(&mut self) -> &mut Self::Target {
6012 &mut self.0
6013 }
6014}
6015
6016impl InvalidationRegion for BracketPairState {
6017 fn ranges(&self) -> &[Range<Anchor>] {
6018 &self.ranges
6019 }
6020}
6021
6022impl InvalidationRegion for SnippetState {
6023 fn ranges(&self) -> &[Range<Anchor>] {
6024 &self.ranges[self.active_index]
6025 }
6026}
6027
6028impl Deref for EditorStyle {
6029 type Target = theme::Editor;
6030
6031 fn deref(&self) -> &Self::Target {
6032 &self.theme
6033 }
6034}
6035
6036pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
6037 let mut highlighted_lines = Vec::new();
6038 for line in diagnostic.message.lines() {
6039 highlighted_lines.push(highlight_diagnostic_message(line));
6040 }
6041
6042 Arc::new(move |cx: &mut BlockContext| {
6043 let settings = cx.global::<Settings>();
6044 let theme = &settings.theme.editor;
6045 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
6046 let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
6047 Flex::column()
6048 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
6049 Label::new(
6050 line.clone(),
6051 style.message.clone().with_font_size(font_size),
6052 )
6053 .with_highlights(highlights.clone())
6054 .contained()
6055 .with_margin_left(cx.anchor_x)
6056 .boxed()
6057 }))
6058 .aligned()
6059 .left()
6060 .boxed()
6061 })
6062}
6063
6064pub fn highlight_diagnostic_message(message: &str) -> (String, Vec<usize>) {
6065 let mut message_without_backticks = String::new();
6066 let mut prev_offset = 0;
6067 let mut inside_block = false;
6068 let mut highlights = Vec::new();
6069 for (match_ix, (offset, _)) in message
6070 .match_indices('`')
6071 .chain([(message.len(), "")])
6072 .enumerate()
6073 {
6074 message_without_backticks.push_str(&message[prev_offset..offset]);
6075 if inside_block {
6076 highlights.extend(prev_offset - match_ix..offset - match_ix);
6077 }
6078
6079 inside_block = !inside_block;
6080 prev_offset = offset + 1;
6081 }
6082
6083 (message_without_backticks, highlights)
6084}
6085
6086pub fn diagnostic_style(
6087 severity: DiagnosticSeverity,
6088 valid: bool,
6089 theme: &theme::Editor,
6090) -> DiagnosticStyle {
6091 match (severity, valid) {
6092 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
6093 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
6094 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
6095 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
6096 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
6097 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
6098 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
6099 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
6100 _ => theme.invalid_hint_diagnostic.clone(),
6101 }
6102}
6103
6104pub fn combine_syntax_and_fuzzy_match_highlights(
6105 text: &str,
6106 default_style: HighlightStyle,
6107 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
6108 match_indices: &[usize],
6109) -> Vec<(Range<usize>, HighlightStyle)> {
6110 let mut result = Vec::new();
6111 let mut match_indices = match_indices.iter().copied().peekable();
6112
6113 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
6114 {
6115 syntax_highlight.weight = None;
6116
6117 // Add highlights for any fuzzy match characters before the next
6118 // syntax highlight range.
6119 while let Some(&match_index) = match_indices.peek() {
6120 if match_index >= range.start {
6121 break;
6122 }
6123 match_indices.next();
6124 let end_index = char_ix_after(match_index, text);
6125 let mut match_style = default_style;
6126 match_style.weight = Some(fonts::Weight::BOLD);
6127 result.push((match_index..end_index, match_style));
6128 }
6129
6130 if range.start == usize::MAX {
6131 break;
6132 }
6133
6134 // Add highlights for any fuzzy match characters within the
6135 // syntax highlight range.
6136 let mut offset = range.start;
6137 while let Some(&match_index) = match_indices.peek() {
6138 if match_index >= range.end {
6139 break;
6140 }
6141
6142 match_indices.next();
6143 if match_index > offset {
6144 result.push((offset..match_index, syntax_highlight));
6145 }
6146
6147 let mut end_index = char_ix_after(match_index, text);
6148 while let Some(&next_match_index) = match_indices.peek() {
6149 if next_match_index == end_index && next_match_index < range.end {
6150 end_index = char_ix_after(next_match_index, text);
6151 match_indices.next();
6152 } else {
6153 break;
6154 }
6155 }
6156
6157 let mut match_style = syntax_highlight;
6158 match_style.weight = Some(fonts::Weight::BOLD);
6159 result.push((match_index..end_index, match_style));
6160 offset = end_index;
6161 }
6162
6163 if offset < range.end {
6164 result.push((offset..range.end, syntax_highlight));
6165 }
6166 }
6167
6168 fn char_ix_after(ix: usize, text: &str) -> usize {
6169 ix + text[ix..].chars().next().unwrap().len_utf8()
6170 }
6171
6172 result
6173}
6174
6175pub fn styled_runs_for_code_label<'a>(
6176 label: &'a CodeLabel,
6177 syntax_theme: &'a theme::SyntaxTheme,
6178) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
6179 let fade_out = HighlightStyle {
6180 fade_out: Some(0.35),
6181 ..Default::default()
6182 };
6183
6184 let mut prev_end = label.filter_range.end;
6185 label
6186 .runs
6187 .iter()
6188 .enumerate()
6189 .flat_map(move |(ix, (range, highlight_id))| {
6190 let style = if let Some(style) = highlight_id.style(syntax_theme) {
6191 style
6192 } else {
6193 return Default::default();
6194 };
6195 let mut muted_style = style.clone();
6196 muted_style.highlight(fade_out);
6197
6198 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
6199 if range.start >= label.filter_range.end {
6200 if range.start > prev_end {
6201 runs.push((prev_end..range.start, fade_out));
6202 }
6203 runs.push((range.clone(), muted_style));
6204 } else if range.end <= label.filter_range.end {
6205 runs.push((range.clone(), style));
6206 } else {
6207 runs.push((range.start..label.filter_range.end, style));
6208 runs.push((label.filter_range.end..range.end, muted_style));
6209 }
6210 prev_end = cmp::max(prev_end, range.end);
6211
6212 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
6213 runs.push((prev_end..label.text.len(), fade_out));
6214 }
6215
6216 runs
6217 })
6218}
6219
6220#[cfg(test)]
6221mod tests {
6222 use crate::test::{
6223 assert_text_with_selections, build_editor, select_ranges, EditorTestContext,
6224 };
6225
6226 use super::*;
6227 use futures::StreamExt;
6228 use gpui::{
6229 geometry::rect::RectF,
6230 platform::{WindowBounds, WindowOptions},
6231 };
6232 use indoc::indoc;
6233 use language::{FakeLspAdapter, LanguageConfig};
6234 use lsp::FakeLanguageServer;
6235 use project::FakeFs;
6236 use settings::LanguageSettings;
6237 use std::{cell::RefCell, rc::Rc, time::Instant};
6238 use text::Point;
6239 use unindent::Unindent;
6240 use util::{
6241 assert_set_eq,
6242 test::{marked_text_by, marked_text_ranges, marked_text_ranges_by, sample_text},
6243 };
6244 use workspace::{FollowableItem, ItemHandle};
6245
6246 #[gpui::test]
6247 fn test_edit_events(cx: &mut MutableAppContext) {
6248 cx.set_global(Settings::test(cx));
6249 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6250
6251 let events = Rc::new(RefCell::new(Vec::new()));
6252 let (_, editor1) = cx.add_window(Default::default(), {
6253 let events = events.clone();
6254 |cx| {
6255 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6256 if matches!(
6257 event,
6258 Event::Edited | Event::BufferEdited | Event::DirtyChanged
6259 ) {
6260 events.borrow_mut().push(("editor1", *event));
6261 }
6262 })
6263 .detach();
6264 Editor::for_buffer(buffer.clone(), None, cx)
6265 }
6266 });
6267 let (_, editor2) = cx.add_window(Default::default(), {
6268 let events = events.clone();
6269 |cx| {
6270 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6271 if matches!(
6272 event,
6273 Event::Edited | Event::BufferEdited | Event::DirtyChanged
6274 ) {
6275 events.borrow_mut().push(("editor2", *event));
6276 }
6277 })
6278 .detach();
6279 Editor::for_buffer(buffer.clone(), None, cx)
6280 }
6281 });
6282 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6283
6284 // Mutating editor 1 will emit an `Edited` event only for that editor.
6285 editor1.update(cx, |editor, cx| editor.insert("X", cx));
6286 assert_eq!(
6287 mem::take(&mut *events.borrow_mut()),
6288 [
6289 ("editor1", Event::Edited),
6290 ("editor1", Event::BufferEdited),
6291 ("editor2", Event::BufferEdited),
6292 ("editor1", Event::DirtyChanged),
6293 ("editor2", Event::DirtyChanged)
6294 ]
6295 );
6296
6297 // Mutating editor 2 will emit an `Edited` event only for that editor.
6298 editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
6299 assert_eq!(
6300 mem::take(&mut *events.borrow_mut()),
6301 [
6302 ("editor2", Event::Edited),
6303 ("editor1", Event::BufferEdited),
6304 ("editor2", Event::BufferEdited),
6305 ]
6306 );
6307
6308 // Undoing on editor 1 will emit an `Edited` event only for that editor.
6309 editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
6310 assert_eq!(
6311 mem::take(&mut *events.borrow_mut()),
6312 [
6313 ("editor1", Event::Edited),
6314 ("editor1", Event::BufferEdited),
6315 ("editor2", Event::BufferEdited),
6316 ("editor1", Event::DirtyChanged),
6317 ("editor2", Event::DirtyChanged),
6318 ]
6319 );
6320
6321 // Redoing on editor 1 will emit an `Edited` event only for that editor.
6322 editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
6323 assert_eq!(
6324 mem::take(&mut *events.borrow_mut()),
6325 [
6326 ("editor1", Event::Edited),
6327 ("editor1", Event::BufferEdited),
6328 ("editor2", Event::BufferEdited),
6329 ("editor1", Event::DirtyChanged),
6330 ("editor2", Event::DirtyChanged),
6331 ]
6332 );
6333
6334 // Undoing on editor 2 will emit an `Edited` event only for that editor.
6335 editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
6336 assert_eq!(
6337 mem::take(&mut *events.borrow_mut()),
6338 [
6339 ("editor2", Event::Edited),
6340 ("editor1", Event::BufferEdited),
6341 ("editor2", Event::BufferEdited),
6342 ("editor1", Event::DirtyChanged),
6343 ("editor2", Event::DirtyChanged),
6344 ]
6345 );
6346
6347 // Redoing on editor 2 will emit an `Edited` event only for that editor.
6348 editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
6349 assert_eq!(
6350 mem::take(&mut *events.borrow_mut()),
6351 [
6352 ("editor2", Event::Edited),
6353 ("editor1", Event::BufferEdited),
6354 ("editor2", Event::BufferEdited),
6355 ("editor1", Event::DirtyChanged),
6356 ("editor2", Event::DirtyChanged),
6357 ]
6358 );
6359
6360 // No event is emitted when the mutation is a no-op.
6361 editor2.update(cx, |editor, cx| {
6362 editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
6363
6364 editor.backspace(&Backspace, cx);
6365 });
6366 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6367 }
6368
6369 #[gpui::test]
6370 fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
6371 cx.set_global(Settings::test(cx));
6372 let mut now = Instant::now();
6373 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6374 let group_interval = buffer.read(cx).transaction_group_interval();
6375 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6376 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6377
6378 editor.update(cx, |editor, cx| {
6379 editor.start_transaction_at(now, cx);
6380 editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
6381
6382 editor.insert("cd", cx);
6383 editor.end_transaction_at(now, cx);
6384 assert_eq!(editor.text(cx), "12cd56");
6385 assert_eq!(editor.selections.ranges(cx), vec![4..4]);
6386
6387 editor.start_transaction_at(now, cx);
6388 editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
6389 editor.insert("e", cx);
6390 editor.end_transaction_at(now, cx);
6391 assert_eq!(editor.text(cx), "12cde6");
6392 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6393
6394 now += group_interval + Duration::from_millis(1);
6395 editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
6396
6397 // Simulate an edit in another editor
6398 buffer.update(cx, |buffer, cx| {
6399 buffer.start_transaction_at(now, cx);
6400 buffer.edit([(0..1, "a")], cx);
6401 buffer.edit([(1..1, "b")], cx);
6402 buffer.end_transaction_at(now, cx);
6403 });
6404
6405 assert_eq!(editor.text(cx), "ab2cde6");
6406 assert_eq!(editor.selections.ranges(cx), vec![3..3]);
6407
6408 // Last transaction happened past the group interval in a different editor.
6409 // Undo it individually and don't restore selections.
6410 editor.undo(&Undo, cx);
6411 assert_eq!(editor.text(cx), "12cde6");
6412 assert_eq!(editor.selections.ranges(cx), vec![2..2]);
6413
6414 // First two transactions happened within the group interval in this editor.
6415 // Undo them together and restore selections.
6416 editor.undo(&Undo, cx);
6417 editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
6418 assert_eq!(editor.text(cx), "123456");
6419 assert_eq!(editor.selections.ranges(cx), vec![0..0]);
6420
6421 // Redo the first two transactions together.
6422 editor.redo(&Redo, cx);
6423 assert_eq!(editor.text(cx), "12cde6");
6424 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6425
6426 // Redo the last transaction on its own.
6427 editor.redo(&Redo, cx);
6428 assert_eq!(editor.text(cx), "ab2cde6");
6429 assert_eq!(editor.selections.ranges(cx), vec![6..6]);
6430
6431 // Test empty transactions.
6432 editor.start_transaction_at(now, cx);
6433 editor.end_transaction_at(now, cx);
6434 editor.undo(&Undo, cx);
6435 assert_eq!(editor.text(cx), "12cde6");
6436 });
6437 }
6438
6439 #[gpui::test]
6440 fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
6441 cx.set_global(Settings::test(cx));
6442
6443 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
6444 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6445 editor.update(cx, |view, cx| {
6446 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6447 });
6448 assert_eq!(
6449 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6450 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6451 );
6452
6453 editor.update(cx, |view, cx| {
6454 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6455 });
6456
6457 assert_eq!(
6458 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6459 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6460 );
6461
6462 editor.update(cx, |view, cx| {
6463 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6464 });
6465
6466 assert_eq!(
6467 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6468 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6469 );
6470
6471 editor.update(cx, |view, cx| {
6472 view.end_selection(cx);
6473 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6474 });
6475
6476 assert_eq!(
6477 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6478 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6479 );
6480
6481 editor.update(cx, |view, cx| {
6482 view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
6483 view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
6484 });
6485
6486 assert_eq!(
6487 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6488 [
6489 DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
6490 DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
6491 ]
6492 );
6493
6494 editor.update(cx, |view, cx| {
6495 view.end_selection(cx);
6496 });
6497
6498 assert_eq!(
6499 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6500 [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
6501 );
6502 }
6503
6504 #[gpui::test]
6505 fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
6506 cx.set_global(Settings::test(cx));
6507 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6508 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6509
6510 view.update(cx, |view, cx| {
6511 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6512 assert_eq!(
6513 view.selections.display_ranges(cx),
6514 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6515 );
6516 });
6517
6518 view.update(cx, |view, cx| {
6519 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6520 assert_eq!(
6521 view.selections.display_ranges(cx),
6522 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6523 );
6524 });
6525
6526 view.update(cx, |view, cx| {
6527 view.cancel(&Cancel, cx);
6528 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6529 assert_eq!(
6530 view.selections.display_ranges(cx),
6531 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6532 );
6533 });
6534 }
6535
6536 #[gpui::test]
6537 fn test_clone(cx: &mut gpui::MutableAppContext) {
6538 let (text, selection_ranges) = marked_text_ranges(indoc! {"
6539 one
6540 two
6541 three[]
6542 four
6543 five[]
6544 "});
6545 cx.set_global(Settings::test(cx));
6546 let buffer = MultiBuffer::build_simple(&text, cx);
6547
6548 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6549
6550 editor.update(cx, |editor, cx| {
6551 editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
6552 editor.fold_ranges(
6553 [
6554 Point::new(1, 0)..Point::new(2, 0),
6555 Point::new(3, 0)..Point::new(4, 0),
6556 ],
6557 cx,
6558 );
6559 });
6560
6561 let (_, cloned_editor) = editor.update(cx, |editor, cx| {
6562 cx.add_window(Default::default(), |cx| editor.clone(cx))
6563 });
6564
6565 let snapshot = editor.update(cx, |e, cx| e.snapshot(cx));
6566 let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx));
6567
6568 assert_eq!(
6569 cloned_editor.update(cx, |e, cx| e.display_text(cx)),
6570 editor.update(cx, |e, cx| e.display_text(cx))
6571 );
6572 assert_eq!(
6573 cloned_snapshot
6574 .folds_in_range(0..text.len())
6575 .collect::<Vec<_>>(),
6576 snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
6577 );
6578 assert_set_eq!(
6579 cloned_editor.read(cx).selections.ranges::<Point>(cx),
6580 editor.read(cx).selections.ranges(cx)
6581 );
6582 assert_set_eq!(
6583 cloned_editor.update(cx, |e, cx| e.selections.display_ranges(cx)),
6584 editor.update(cx, |e, cx| e.selections.display_ranges(cx))
6585 );
6586 }
6587
6588 #[gpui::test]
6589 fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
6590 cx.set_global(Settings::test(cx));
6591 use workspace::Item;
6592 let nav_history = Rc::new(RefCell::new(workspace::NavHistory::default()));
6593 let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
6594
6595 cx.add_window(Default::default(), |cx| {
6596 let mut editor = build_editor(buffer.clone(), cx);
6597 editor.nav_history = Some(ItemNavHistory::new(nav_history.clone(), &cx.handle()));
6598
6599 // Move the cursor a small distance.
6600 // Nothing is added to the navigation history.
6601 editor.change_selections(None, cx, |s| {
6602 s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
6603 });
6604 editor.change_selections(None, cx, |s| {
6605 s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
6606 });
6607 assert!(nav_history.borrow_mut().pop_backward().is_none());
6608
6609 // Move the cursor a large distance.
6610 // The history can jump back to the previous position.
6611 editor.change_selections(None, cx, |s| {
6612 s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
6613 });
6614 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6615 editor.navigate(nav_entry.data.unwrap(), cx);
6616 assert_eq!(nav_entry.item.id(), cx.view_id());
6617 assert_eq!(
6618 editor.selections.display_ranges(cx),
6619 &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
6620 );
6621 assert!(nav_history.borrow_mut().pop_backward().is_none());
6622
6623 // Move the cursor a small distance via the mouse.
6624 // Nothing is added to the navigation history.
6625 editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
6626 editor.end_selection(cx);
6627 assert_eq!(
6628 editor.selections.display_ranges(cx),
6629 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6630 );
6631 assert!(nav_history.borrow_mut().pop_backward().is_none());
6632
6633 // Move the cursor a large distance via the mouse.
6634 // The history can jump back to the previous position.
6635 editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
6636 editor.end_selection(cx);
6637 assert_eq!(
6638 editor.selections.display_ranges(cx),
6639 &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
6640 );
6641 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6642 editor.navigate(nav_entry.data.unwrap(), cx);
6643 assert_eq!(nav_entry.item.id(), cx.view_id());
6644 assert_eq!(
6645 editor.selections.display_ranges(cx),
6646 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6647 );
6648 assert!(nav_history.borrow_mut().pop_backward().is_none());
6649
6650 // Set scroll position to check later
6651 editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
6652 let original_scroll_position = editor.scroll_position;
6653 let original_scroll_top_anchor = editor.scroll_top_anchor.clone();
6654
6655 // Jump to the end of the document and adjust scroll
6656 editor.move_to_end(&MoveToEnd, cx);
6657 editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx);
6658 assert_ne!(editor.scroll_position, original_scroll_position);
6659 assert_ne!(editor.scroll_top_anchor, original_scroll_top_anchor);
6660
6661 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6662 editor.navigate(nav_entry.data.unwrap(), cx);
6663 assert_eq!(editor.scroll_position, original_scroll_position);
6664 assert_eq!(editor.scroll_top_anchor, original_scroll_top_anchor);
6665
6666 // Ensure we don't panic when navigation data contains invalid anchors *and* points.
6667 let mut invalid_anchor = editor.scroll_top_anchor.clone();
6668 invalid_anchor.text_anchor.buffer_id = Some(999);
6669 let invalid_point = Point::new(9999, 0);
6670 editor.navigate(
6671 Box::new(NavigationData {
6672 cursor_anchor: invalid_anchor.clone(),
6673 cursor_position: invalid_point,
6674 scroll_top_anchor: invalid_anchor.clone(),
6675 scroll_top_row: invalid_point.row,
6676 scroll_position: Default::default(),
6677 }),
6678 cx,
6679 );
6680 assert_eq!(
6681 editor.selections.display_ranges(cx),
6682 &[editor.max_point(cx)..editor.max_point(cx)]
6683 );
6684 assert_eq!(
6685 editor.scroll_position(cx),
6686 vec2f(0., editor.max_point(cx).row() as f32)
6687 );
6688
6689 editor
6690 });
6691 }
6692
6693 #[gpui::test]
6694 fn test_cancel(cx: &mut gpui::MutableAppContext) {
6695 cx.set_global(Settings::test(cx));
6696 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6697 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6698
6699 view.update(cx, |view, cx| {
6700 view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
6701 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6702 view.end_selection(cx);
6703
6704 view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
6705 view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
6706 view.end_selection(cx);
6707 assert_eq!(
6708 view.selections.display_ranges(cx),
6709 [
6710 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
6711 DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
6712 ]
6713 );
6714 });
6715
6716 view.update(cx, |view, cx| {
6717 view.cancel(&Cancel, cx);
6718 assert_eq!(
6719 view.selections.display_ranges(cx),
6720 [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
6721 );
6722 });
6723
6724 view.update(cx, |view, cx| {
6725 view.cancel(&Cancel, cx);
6726 assert_eq!(
6727 view.selections.display_ranges(cx),
6728 [DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1)]
6729 );
6730 });
6731 }
6732
6733 #[gpui::test]
6734 fn test_fold(cx: &mut gpui::MutableAppContext) {
6735 cx.set_global(Settings::test(cx));
6736 let buffer = MultiBuffer::build_simple(
6737 &"
6738 impl Foo {
6739 // Hello!
6740
6741 fn a() {
6742 1
6743 }
6744
6745 fn b() {
6746 2
6747 }
6748
6749 fn c() {
6750 3
6751 }
6752 }
6753 "
6754 .unindent(),
6755 cx,
6756 );
6757 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6758
6759 view.update(cx, |view, cx| {
6760 view.change_selections(None, cx, |s| {
6761 s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
6762 });
6763 view.fold(&Fold, cx);
6764 assert_eq!(
6765 view.display_text(cx),
6766 "
6767 impl Foo {
6768 // Hello!
6769
6770 fn a() {
6771 1
6772 }
6773
6774 fn b() {…
6775 }
6776
6777 fn c() {…
6778 }
6779 }
6780 "
6781 .unindent(),
6782 );
6783
6784 view.fold(&Fold, cx);
6785 assert_eq!(
6786 view.display_text(cx),
6787 "
6788 impl Foo {…
6789 }
6790 "
6791 .unindent(),
6792 );
6793
6794 view.unfold_lines(&UnfoldLines, cx);
6795 assert_eq!(
6796 view.display_text(cx),
6797 "
6798 impl Foo {
6799 // Hello!
6800
6801 fn a() {
6802 1
6803 }
6804
6805 fn b() {…
6806 }
6807
6808 fn c() {…
6809 }
6810 }
6811 "
6812 .unindent(),
6813 );
6814
6815 view.unfold_lines(&UnfoldLines, cx);
6816 assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
6817 });
6818 }
6819
6820 #[gpui::test]
6821 fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
6822 cx.set_global(Settings::test(cx));
6823 let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
6824 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6825
6826 buffer.update(cx, |buffer, cx| {
6827 buffer.edit(
6828 vec![
6829 (Point::new(1, 0)..Point::new(1, 0), "\t"),
6830 (Point::new(1, 1)..Point::new(1, 1), "\t"),
6831 ],
6832 cx,
6833 );
6834 });
6835
6836 view.update(cx, |view, cx| {
6837 assert_eq!(
6838 view.selections.display_ranges(cx),
6839 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6840 );
6841
6842 view.move_down(&MoveDown, cx);
6843 assert_eq!(
6844 view.selections.display_ranges(cx),
6845 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
6846 );
6847
6848 view.move_right(&MoveRight, cx);
6849 assert_eq!(
6850 view.selections.display_ranges(cx),
6851 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
6852 );
6853
6854 view.move_left(&MoveLeft, cx);
6855 assert_eq!(
6856 view.selections.display_ranges(cx),
6857 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
6858 );
6859
6860 view.move_up(&MoveUp, cx);
6861 assert_eq!(
6862 view.selections.display_ranges(cx),
6863 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6864 );
6865
6866 view.move_to_end(&MoveToEnd, cx);
6867 assert_eq!(
6868 view.selections.display_ranges(cx),
6869 &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
6870 );
6871
6872 view.move_to_beginning(&MoveToBeginning, cx);
6873 assert_eq!(
6874 view.selections.display_ranges(cx),
6875 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6876 );
6877
6878 view.change_selections(None, cx, |s| {
6879 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
6880 });
6881 view.select_to_beginning(&SelectToBeginning, cx);
6882 assert_eq!(
6883 view.selections.display_ranges(cx),
6884 &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
6885 );
6886
6887 view.select_to_end(&SelectToEnd, cx);
6888 assert_eq!(
6889 view.selections.display_ranges(cx),
6890 &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
6891 );
6892 });
6893 }
6894
6895 #[gpui::test]
6896 fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
6897 cx.set_global(Settings::test(cx));
6898 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
6899 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6900
6901 assert_eq!('ⓐ'.len_utf8(), 3);
6902 assert_eq!('α'.len_utf8(), 2);
6903
6904 view.update(cx, |view, cx| {
6905 view.fold_ranges(
6906 vec![
6907 Point::new(0, 6)..Point::new(0, 12),
6908 Point::new(1, 2)..Point::new(1, 4),
6909 Point::new(2, 4)..Point::new(2, 8),
6910 ],
6911 cx,
6912 );
6913 assert_eq!(view.display_text(cx), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\n");
6914
6915 view.move_right(&MoveRight, cx);
6916 assert_eq!(
6917 view.selections.display_ranges(cx),
6918 &[empty_range(0, "ⓐ".len())]
6919 );
6920 view.move_right(&MoveRight, cx);
6921 assert_eq!(
6922 view.selections.display_ranges(cx),
6923 &[empty_range(0, "ⓐⓑ".len())]
6924 );
6925 view.move_right(&MoveRight, cx);
6926 assert_eq!(
6927 view.selections.display_ranges(cx),
6928 &[empty_range(0, "ⓐⓑ…".len())]
6929 );
6930
6931 view.move_down(&MoveDown, cx);
6932 assert_eq!(
6933 view.selections.display_ranges(cx),
6934 &[empty_range(1, "ab…".len())]
6935 );
6936 view.move_left(&MoveLeft, cx);
6937 assert_eq!(
6938 view.selections.display_ranges(cx),
6939 &[empty_range(1, "ab".len())]
6940 );
6941 view.move_left(&MoveLeft, cx);
6942 assert_eq!(
6943 view.selections.display_ranges(cx),
6944 &[empty_range(1, "a".len())]
6945 );
6946
6947 view.move_down(&MoveDown, cx);
6948 assert_eq!(
6949 view.selections.display_ranges(cx),
6950 &[empty_range(2, "α".len())]
6951 );
6952 view.move_right(&MoveRight, cx);
6953 assert_eq!(
6954 view.selections.display_ranges(cx),
6955 &[empty_range(2, "αβ".len())]
6956 );
6957 view.move_right(&MoveRight, cx);
6958 assert_eq!(
6959 view.selections.display_ranges(cx),
6960 &[empty_range(2, "αβ…".len())]
6961 );
6962 view.move_right(&MoveRight, cx);
6963 assert_eq!(
6964 view.selections.display_ranges(cx),
6965 &[empty_range(2, "αβ…ε".len())]
6966 );
6967
6968 view.move_up(&MoveUp, cx);
6969 assert_eq!(
6970 view.selections.display_ranges(cx),
6971 &[empty_range(1, "ab…e".len())]
6972 );
6973 view.move_up(&MoveUp, cx);
6974 assert_eq!(
6975 view.selections.display_ranges(cx),
6976 &[empty_range(0, "ⓐⓑ…ⓔ".len())]
6977 );
6978 view.move_left(&MoveLeft, cx);
6979 assert_eq!(
6980 view.selections.display_ranges(cx),
6981 &[empty_range(0, "ⓐⓑ…".len())]
6982 );
6983 view.move_left(&MoveLeft, cx);
6984 assert_eq!(
6985 view.selections.display_ranges(cx),
6986 &[empty_range(0, "ⓐⓑ".len())]
6987 );
6988 view.move_left(&MoveLeft, cx);
6989 assert_eq!(
6990 view.selections.display_ranges(cx),
6991 &[empty_range(0, "ⓐ".len())]
6992 );
6993 });
6994 }
6995
6996 #[gpui::test]
6997 fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
6998 cx.set_global(Settings::test(cx));
6999 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
7000 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7001 view.update(cx, |view, cx| {
7002 view.change_selections(None, cx, |s| {
7003 s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
7004 });
7005 view.move_down(&MoveDown, cx);
7006 assert_eq!(
7007 view.selections.display_ranges(cx),
7008 &[empty_range(1, "abcd".len())]
7009 );
7010
7011 view.move_down(&MoveDown, cx);
7012 assert_eq!(
7013 view.selections.display_ranges(cx),
7014 &[empty_range(2, "αβγ".len())]
7015 );
7016
7017 view.move_down(&MoveDown, cx);
7018 assert_eq!(
7019 view.selections.display_ranges(cx),
7020 &[empty_range(3, "abcd".len())]
7021 );
7022
7023 view.move_down(&MoveDown, cx);
7024 assert_eq!(
7025 view.selections.display_ranges(cx),
7026 &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
7027 );
7028
7029 view.move_up(&MoveUp, cx);
7030 assert_eq!(
7031 view.selections.display_ranges(cx),
7032 &[empty_range(3, "abcd".len())]
7033 );
7034
7035 view.move_up(&MoveUp, cx);
7036 assert_eq!(
7037 view.selections.display_ranges(cx),
7038 &[empty_range(2, "αβγ".len())]
7039 );
7040 });
7041 }
7042
7043 #[gpui::test]
7044 fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
7045 cx.set_global(Settings::test(cx));
7046 let buffer = MultiBuffer::build_simple("abc\n def", cx);
7047 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7048 view.update(cx, |view, cx| {
7049 view.change_selections(None, cx, |s| {
7050 s.select_display_ranges([
7051 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7052 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7053 ]);
7054 });
7055 });
7056
7057 view.update(cx, |view, cx| {
7058 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7059 assert_eq!(
7060 view.selections.display_ranges(cx),
7061 &[
7062 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7063 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7064 ]
7065 );
7066 });
7067
7068 view.update(cx, |view, cx| {
7069 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7070 assert_eq!(
7071 view.selections.display_ranges(cx),
7072 &[
7073 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7074 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7075 ]
7076 );
7077 });
7078
7079 view.update(cx, |view, cx| {
7080 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7081 assert_eq!(
7082 view.selections.display_ranges(cx),
7083 &[
7084 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7085 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7086 ]
7087 );
7088 });
7089
7090 view.update(cx, |view, cx| {
7091 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7092 assert_eq!(
7093 view.selections.display_ranges(cx),
7094 &[
7095 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7096 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7097 ]
7098 );
7099 });
7100
7101 // Moving to the end of line again is a no-op.
7102 view.update(cx, |view, cx| {
7103 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7104 assert_eq!(
7105 view.selections.display_ranges(cx),
7106 &[
7107 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7108 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7109 ]
7110 );
7111 });
7112
7113 view.update(cx, |view, cx| {
7114 view.move_left(&MoveLeft, cx);
7115 view.select_to_beginning_of_line(
7116 &SelectToBeginningOfLine {
7117 stop_at_soft_wraps: true,
7118 },
7119 cx,
7120 );
7121 assert_eq!(
7122 view.selections.display_ranges(cx),
7123 &[
7124 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7125 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7126 ]
7127 );
7128 });
7129
7130 view.update(cx, |view, cx| {
7131 view.select_to_beginning_of_line(
7132 &SelectToBeginningOfLine {
7133 stop_at_soft_wraps: true,
7134 },
7135 cx,
7136 );
7137 assert_eq!(
7138 view.selections.display_ranges(cx),
7139 &[
7140 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7141 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
7142 ]
7143 );
7144 });
7145
7146 view.update(cx, |view, cx| {
7147 view.select_to_beginning_of_line(
7148 &SelectToBeginningOfLine {
7149 stop_at_soft_wraps: true,
7150 },
7151 cx,
7152 );
7153 assert_eq!(
7154 view.selections.display_ranges(cx),
7155 &[
7156 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7157 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7158 ]
7159 );
7160 });
7161
7162 view.update(cx, |view, cx| {
7163 view.select_to_end_of_line(
7164 &SelectToEndOfLine {
7165 stop_at_soft_wraps: true,
7166 },
7167 cx,
7168 );
7169 assert_eq!(
7170 view.selections.display_ranges(cx),
7171 &[
7172 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
7173 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
7174 ]
7175 );
7176 });
7177
7178 view.update(cx, |view, cx| {
7179 view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
7180 assert_eq!(view.display_text(cx), "ab\n de");
7181 assert_eq!(
7182 view.selections.display_ranges(cx),
7183 &[
7184 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7185 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7186 ]
7187 );
7188 });
7189
7190 view.update(cx, |view, cx| {
7191 view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7192 assert_eq!(view.display_text(cx), "\n");
7193 assert_eq!(
7194 view.selections.display_ranges(cx),
7195 &[
7196 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7197 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7198 ]
7199 );
7200 });
7201 }
7202
7203 #[gpui::test]
7204 fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
7205 cx.set_global(Settings::test(cx));
7206 let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
7207 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7208 view.update(cx, |view, cx| {
7209 view.change_selections(None, cx, |s| {
7210 s.select_display_ranges([
7211 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
7212 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
7213 ])
7214 });
7215
7216 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7217 assert_selection_ranges(
7218 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7219 vec![('<', '>'), ('[', ']')],
7220 view,
7221 cx,
7222 );
7223
7224 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7225 assert_selection_ranges(
7226 "use std<>::str::{foo, bar}\n\n []{baz.qux()}",
7227 vec![('<', '>'), ('[', ']')],
7228 view,
7229 cx,
7230 );
7231
7232 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7233 assert_selection_ranges(
7234 "use <>std::str::{foo, bar}\n\n[] {baz.qux()}",
7235 vec![('<', '>'), ('[', ']')],
7236 view,
7237 cx,
7238 );
7239
7240 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7241 assert_selection_ranges(
7242 "<>use std::str::{foo, bar}\n[]\n {baz.qux()}",
7243 vec![('<', '>'), ('[', ']')],
7244 view,
7245 cx,
7246 );
7247
7248 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7249 assert_selection_ranges(
7250 "<>use std::str::{foo, bar[]}\n\n {baz.qux()}",
7251 vec![('<', '>'), ('[', ']')],
7252 view,
7253 cx,
7254 );
7255
7256 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7257 assert_selection_ranges(
7258 "use<> std::str::{foo, bar}[]\n\n {baz.qux()}",
7259 vec![('<', '>'), ('[', ']')],
7260 view,
7261 cx,
7262 );
7263
7264 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7265 assert_selection_ranges(
7266 "use std<>::str::{foo, bar}\n[]\n {baz.qux()}",
7267 vec![('<', '>'), ('[', ']')],
7268 view,
7269 cx,
7270 );
7271
7272 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7273 assert_selection_ranges(
7274 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7275 vec![('<', '>'), ('[', ']')],
7276 view,
7277 cx,
7278 );
7279
7280 view.move_right(&MoveRight, cx);
7281 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7282 assert_selection_ranges(
7283 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7284 vec![('<', '>'), ('[', ']')],
7285 view,
7286 cx,
7287 );
7288
7289 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7290 assert_selection_ranges(
7291 "use std>::s<tr::{foo, bar}\n\n ]{b[az.qux()}",
7292 vec![('<', '>'), ('[', ']')],
7293 view,
7294 cx,
7295 );
7296
7297 view.select_to_next_word_end(&SelectToNextWordEnd, cx);
7298 assert_selection_ranges(
7299 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7300 vec![('<', '>'), ('[', ']')],
7301 view,
7302 cx,
7303 );
7304 });
7305 }
7306
7307 #[gpui::test]
7308 fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
7309 cx.set_global(Settings::test(cx));
7310 let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
7311 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7312
7313 view.update(cx, |view, cx| {
7314 view.set_wrap_width(Some(140.), cx);
7315 assert_eq!(
7316 view.display_text(cx),
7317 "use one::{\n two::three::\n four::five\n};"
7318 );
7319
7320 view.change_selections(None, cx, |s| {
7321 s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
7322 });
7323
7324 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7325 assert_eq!(
7326 view.selections.display_ranges(cx),
7327 &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
7328 );
7329
7330 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7331 assert_eq!(
7332 view.selections.display_ranges(cx),
7333 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7334 );
7335
7336 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7337 assert_eq!(
7338 view.selections.display_ranges(cx),
7339 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7340 );
7341
7342 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7343 assert_eq!(
7344 view.selections.display_ranges(cx),
7345 &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
7346 );
7347
7348 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7349 assert_eq!(
7350 view.selections.display_ranges(cx),
7351 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7352 );
7353
7354 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7355 assert_eq!(
7356 view.selections.display_ranges(cx),
7357 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7358 );
7359 });
7360 }
7361
7362 #[gpui::test]
7363 fn test_delete_to_beginning_of_line(cx: &mut gpui::MutableAppContext) {
7364 cx.set_global(Settings::test(cx));
7365 let (text, ranges) = marked_text_ranges("one [two three] four");
7366 let buffer = MultiBuffer::build_simple(&text, cx);
7367
7368 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7369
7370 editor.update(cx, |editor, cx| {
7371 editor.change_selections(None, cx, |s| s.select_ranges(ranges));
7372 editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7373 assert_eq!(editor.text(cx), " four");
7374 });
7375 }
7376
7377 #[gpui::test]
7378 fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
7379 cx.set_global(Settings::test(cx));
7380 let buffer = MultiBuffer::build_simple("one two three four", cx);
7381 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7382
7383 view.update(cx, |view, cx| {
7384 view.change_selections(None, cx, |s| {
7385 s.select_display_ranges([
7386 // an empty selection - the preceding word fragment is deleted
7387 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7388 // characters selected - they are deleted
7389 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
7390 ])
7391 });
7392 view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
7393 });
7394
7395 assert_eq!(buffer.read(cx).read(cx).text(), "e two te four");
7396
7397 view.update(cx, |view, cx| {
7398 view.change_selections(None, cx, |s| {
7399 s.select_display_ranges([
7400 // an empty selection - the following word fragment is deleted
7401 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7402 // characters selected - they are deleted
7403 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
7404 ])
7405 });
7406 view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
7407 });
7408
7409 assert_eq!(buffer.read(cx).read(cx).text(), "e t te our");
7410 }
7411
7412 #[gpui::test]
7413 fn test_newline(cx: &mut gpui::MutableAppContext) {
7414 cx.set_global(Settings::test(cx));
7415 let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
7416 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7417
7418 view.update(cx, |view, cx| {
7419 view.change_selections(None, cx, |s| {
7420 s.select_display_ranges([
7421 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7422 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7423 DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
7424 ])
7425 });
7426
7427 view.newline(&Newline, cx);
7428 assert_eq!(view.text(cx), "aa\naa\n \n bb\n bb\n");
7429 });
7430 }
7431
7432 #[gpui::test]
7433 fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) {
7434 cx.set_global(Settings::test(cx));
7435 let buffer = MultiBuffer::build_simple(
7436 "
7437 a
7438 b(
7439 X
7440 )
7441 c(
7442 X
7443 )
7444 "
7445 .unindent()
7446 .as_str(),
7447 cx,
7448 );
7449
7450 let (_, editor) = cx.add_window(Default::default(), |cx| {
7451 let mut editor = build_editor(buffer.clone(), cx);
7452 editor.change_selections(None, cx, |s| {
7453 s.select_ranges([
7454 Point::new(2, 4)..Point::new(2, 5),
7455 Point::new(5, 4)..Point::new(5, 5),
7456 ])
7457 });
7458 editor
7459 });
7460
7461 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7462 buffer.update(cx, |buffer, cx| {
7463 buffer.edit(
7464 [
7465 (Point::new(1, 2)..Point::new(3, 0), ""),
7466 (Point::new(4, 2)..Point::new(6, 0), ""),
7467 ],
7468 cx,
7469 );
7470 assert_eq!(
7471 buffer.read(cx).text(),
7472 "
7473 a
7474 b()
7475 c()
7476 "
7477 .unindent()
7478 );
7479 });
7480
7481 editor.update(cx, |editor, cx| {
7482 assert_eq!(
7483 editor.selections.ranges(cx),
7484 &[
7485 Point::new(1, 2)..Point::new(1, 2),
7486 Point::new(2, 2)..Point::new(2, 2),
7487 ],
7488 );
7489
7490 editor.newline(&Newline, cx);
7491 assert_eq!(
7492 editor.text(cx),
7493 "
7494 a
7495 b(
7496 )
7497 c(
7498 )
7499 "
7500 .unindent()
7501 );
7502
7503 // The selections are moved after the inserted newlines
7504 assert_eq!(
7505 editor.selections.ranges(cx),
7506 &[
7507 Point::new(2, 0)..Point::new(2, 0),
7508 Point::new(4, 0)..Point::new(4, 0),
7509 ],
7510 );
7511 });
7512 }
7513
7514 #[gpui::test]
7515 fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) {
7516 cx.set_global(Settings::test(cx));
7517 let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
7518 let (_, editor) = cx.add_window(Default::default(), |cx| {
7519 let mut editor = build_editor(buffer.clone(), cx);
7520 editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
7521 editor
7522 });
7523
7524 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7525 buffer.update(cx, |buffer, cx| {
7526 buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], cx);
7527 assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
7528 });
7529
7530 editor.update(cx, |editor, cx| {
7531 assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
7532
7533 editor.insert("Z", cx);
7534 assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
7535
7536 // The selections are moved after the inserted characters
7537 assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
7538 });
7539 }
7540
7541 #[gpui::test]
7542 async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
7543 let mut cx = EditorTestContext::new(cx).await;
7544
7545 cx.set_state(indoc! {"
7546 [one} [two}
7547 three
7548 four"});
7549 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7550 cx.assert_editor_state(indoc! {"
7551 [one} [two}
7552 three
7553 four"});
7554
7555 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7556 cx.assert_editor_state(indoc! {"
7557 [one} [two}
7558 three
7559 four"});
7560
7561 // select across line ending
7562 cx.set_state(indoc! {"
7563 one two
7564 t[hree
7565 } four"});
7566 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7567 cx.assert_editor_state(indoc! {"
7568 one two
7569 t[hree
7570 } four"});
7571
7572 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7573 cx.assert_editor_state(indoc! {"
7574 one two
7575 t[hree
7576 } four"});
7577
7578 // Ensure that indenting/outdenting works when the cursor is at column 0.
7579 cx.set_state(indoc! {"
7580 one two
7581 |three
7582 four"});
7583 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7584 cx.assert_editor_state(indoc! {"
7585 one two
7586 |three
7587 four"});
7588
7589 cx.set_state(indoc! {"
7590 one two
7591 | three
7592 four"});
7593 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7594 cx.assert_editor_state(indoc! {"
7595 one two
7596 |three
7597 four"});
7598 }
7599
7600 #[gpui::test]
7601 async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
7602 let mut cx = EditorTestContext::new(cx).await;
7603 cx.update(|cx| {
7604 cx.update_global::<Settings, _, _>(|settings, _| {
7605 settings.language_settings.hard_tabs = Some(true);
7606 });
7607 });
7608
7609 // select two ranges on one line
7610 cx.set_state(indoc! {"
7611 [one} [two}
7612 three
7613 four"});
7614 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7615 cx.assert_editor_state(indoc! {"
7616 \t[one} [two}
7617 three
7618 four"});
7619 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7620 cx.assert_editor_state(indoc! {"
7621 \t\t[one} [two}
7622 three
7623 four"});
7624 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7625 cx.assert_editor_state(indoc! {"
7626 \t[one} [two}
7627 three
7628 four"});
7629 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7630 cx.assert_editor_state(indoc! {"
7631 [one} [two}
7632 three
7633 four"});
7634
7635 // select across a line ending
7636 cx.set_state(indoc! {"
7637 one two
7638 t[hree
7639 }four"});
7640 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7641 cx.assert_editor_state(indoc! {"
7642 one two
7643 \tt[hree
7644 }four"});
7645 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7646 cx.assert_editor_state(indoc! {"
7647 one two
7648 \t\tt[hree
7649 }four"});
7650 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7651 cx.assert_editor_state(indoc! {"
7652 one two
7653 \tt[hree
7654 }four"});
7655 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7656 cx.assert_editor_state(indoc! {"
7657 one two
7658 t[hree
7659 }four"});
7660
7661 // Ensure that indenting/outdenting works when the cursor is at column 0.
7662 cx.set_state(indoc! {"
7663 one two
7664 |three
7665 four"});
7666 cx.assert_editor_state(indoc! {"
7667 one two
7668 |three
7669 four"});
7670 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7671 cx.assert_editor_state(indoc! {"
7672 one two
7673 \t|three
7674 four"});
7675 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7676 cx.assert_editor_state(indoc! {"
7677 one two
7678 |three
7679 four"});
7680 }
7681
7682 #[gpui::test]
7683 fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
7684 cx.set_global(
7685 Settings::test(cx)
7686 .with_language_defaults(
7687 "TOML",
7688 LanguageSettings {
7689 tab_size: Some(2.try_into().unwrap()),
7690 ..Default::default()
7691 },
7692 )
7693 .with_language_defaults(
7694 "Rust",
7695 LanguageSettings {
7696 tab_size: Some(4.try_into().unwrap()),
7697 ..Default::default()
7698 },
7699 ),
7700 );
7701 let toml_language = Arc::new(Language::new(
7702 LanguageConfig {
7703 name: "TOML".into(),
7704 ..Default::default()
7705 },
7706 None,
7707 ));
7708 let rust_language = Arc::new(Language::new(
7709 LanguageConfig {
7710 name: "Rust".into(),
7711 ..Default::default()
7712 },
7713 None,
7714 ));
7715
7716 let toml_buffer = cx
7717 .add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
7718 let rust_buffer = cx.add_model(|cx| {
7719 Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
7720 });
7721 let multibuffer = cx.add_model(|cx| {
7722 let mut multibuffer = MultiBuffer::new(0);
7723 multibuffer.push_excerpts(
7724 toml_buffer.clone(),
7725 [ExcerptRange {
7726 context: Point::new(0, 0)..Point::new(2, 0),
7727 primary: None,
7728 }],
7729 cx,
7730 );
7731 multibuffer.push_excerpts(
7732 rust_buffer.clone(),
7733 [ExcerptRange {
7734 context: Point::new(0, 0)..Point::new(1, 0),
7735 primary: None,
7736 }],
7737 cx,
7738 );
7739 multibuffer
7740 });
7741
7742 cx.add_window(Default::default(), |cx| {
7743 let mut editor = build_editor(multibuffer, cx);
7744
7745 assert_eq!(
7746 editor.text(cx),
7747 indoc! {"
7748 a = 1
7749 b = 2
7750
7751 const c: usize = 3;
7752 "}
7753 );
7754
7755 select_ranges(
7756 &mut editor,
7757 indoc! {"
7758 [a] = 1
7759 b = 2
7760
7761 [const c:] usize = 3;
7762 "},
7763 cx,
7764 );
7765
7766 editor.tab(&Tab, cx);
7767 assert_text_with_selections(
7768 &mut editor,
7769 indoc! {"
7770 [a] = 1
7771 b = 2
7772
7773 [const c:] usize = 3;
7774 "},
7775 cx,
7776 );
7777 editor.tab_prev(&TabPrev, cx);
7778 assert_text_with_selections(
7779 &mut editor,
7780 indoc! {"
7781 [a] = 1
7782 b = 2
7783
7784 [const c:] usize = 3;
7785 "},
7786 cx,
7787 );
7788
7789 editor
7790 });
7791 }
7792
7793 #[gpui::test]
7794 async fn test_backspace(cx: &mut gpui::TestAppContext) {
7795 let mut cx = EditorTestContext::new(cx).await;
7796 // Basic backspace
7797 cx.set_state(indoc! {"
7798 on|e two three
7799 fou[r} five six
7800 seven {eight nine
7801 ]ten"});
7802 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7803 cx.assert_editor_state(indoc! {"
7804 o|e two three
7805 fou| five six
7806 seven |ten"});
7807
7808 // Test backspace inside and around indents
7809 cx.set_state(indoc! {"
7810 zero
7811 |one
7812 |two
7813 | | | three
7814 | | four"});
7815 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7816 cx.assert_editor_state(indoc! {"
7817 zero
7818 |one
7819 |two
7820 | three| four"});
7821
7822 // Test backspace with line_mode set to true
7823 cx.update_editor(|e, _| e.selections.line_mode = true);
7824 cx.set_state(indoc! {"
7825 The |quick |brown
7826 fox jumps over
7827 the lazy dog
7828 |The qu[ick b}rown"});
7829 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7830 cx.assert_editor_state(indoc! {"
7831 |fox jumps over
7832 the lazy dog|"});
7833 }
7834
7835 #[gpui::test]
7836 async fn test_delete(cx: &mut gpui::TestAppContext) {
7837 let mut cx = EditorTestContext::new(cx).await;
7838
7839 cx.set_state(indoc! {"
7840 on|e two three
7841 fou[r} five six
7842 seven {eight nine
7843 ]ten"});
7844 cx.update_editor(|e, cx| e.delete(&Delete, cx));
7845 cx.assert_editor_state(indoc! {"
7846 on| two three
7847 fou| five six
7848 seven |ten"});
7849
7850 // Test backspace with line_mode set to true
7851 cx.update_editor(|e, _| e.selections.line_mode = true);
7852 cx.set_state(indoc! {"
7853 The |quick |brown
7854 fox {jum]ps over
7855 the lazy dog
7856 |The qu[ick b}rown"});
7857 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7858 cx.assert_editor_state("|the lazy dog|");
7859 }
7860
7861 #[gpui::test]
7862 fn test_delete_line(cx: &mut gpui::MutableAppContext) {
7863 cx.set_global(Settings::test(cx));
7864 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7865 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7866 view.update(cx, |view, cx| {
7867 view.change_selections(None, cx, |s| {
7868 s.select_display_ranges([
7869 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7870 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
7871 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7872 ])
7873 });
7874 view.delete_line(&DeleteLine, cx);
7875 assert_eq!(view.display_text(cx), "ghi");
7876 assert_eq!(
7877 view.selections.display_ranges(cx),
7878 vec![
7879 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7880 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
7881 ]
7882 );
7883 });
7884
7885 cx.set_global(Settings::test(cx));
7886 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7887 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7888 view.update(cx, |view, cx| {
7889 view.change_selections(None, cx, |s| {
7890 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
7891 });
7892 view.delete_line(&DeleteLine, cx);
7893 assert_eq!(view.display_text(cx), "ghi\n");
7894 assert_eq!(
7895 view.selections.display_ranges(cx),
7896 vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
7897 );
7898 });
7899 }
7900
7901 #[gpui::test]
7902 fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
7903 cx.set_global(Settings::test(cx));
7904 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7905 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7906 view.update(cx, |view, cx| {
7907 view.change_selections(None, cx, |s| {
7908 s.select_display_ranges([
7909 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
7910 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7911 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7912 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7913 ])
7914 });
7915 view.duplicate_line(&DuplicateLine, cx);
7916 assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
7917 assert_eq!(
7918 view.selections.display_ranges(cx),
7919 vec![
7920 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
7921 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7922 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7923 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
7924 ]
7925 );
7926 });
7927
7928 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7929 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7930 view.update(cx, |view, cx| {
7931 view.change_selections(None, cx, |s| {
7932 s.select_display_ranges([
7933 DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
7934 DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
7935 ])
7936 });
7937 view.duplicate_line(&DuplicateLine, cx);
7938 assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
7939 assert_eq!(
7940 view.selections.display_ranges(cx),
7941 vec![
7942 DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
7943 DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
7944 ]
7945 );
7946 });
7947 }
7948
7949 #[gpui::test]
7950 fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
7951 cx.set_global(Settings::test(cx));
7952 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
7953 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7954 view.update(cx, |view, cx| {
7955 view.fold_ranges(
7956 vec![
7957 Point::new(0, 2)..Point::new(1, 2),
7958 Point::new(2, 3)..Point::new(4, 1),
7959 Point::new(7, 0)..Point::new(8, 4),
7960 ],
7961 cx,
7962 );
7963 view.change_selections(None, cx, |s| {
7964 s.select_display_ranges([
7965 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7966 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
7967 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
7968 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
7969 ])
7970 });
7971 assert_eq!(
7972 view.display_text(cx),
7973 "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
7974 );
7975
7976 view.move_line_up(&MoveLineUp, cx);
7977 assert_eq!(
7978 view.display_text(cx),
7979 "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
7980 );
7981 assert_eq!(
7982 view.selections.display_ranges(cx),
7983 vec![
7984 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7985 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
7986 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
7987 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
7988 ]
7989 );
7990 });
7991
7992 view.update(cx, |view, cx| {
7993 view.move_line_down(&MoveLineDown, cx);
7994 assert_eq!(
7995 view.display_text(cx),
7996 "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
7997 );
7998 assert_eq!(
7999 view.selections.display_ranges(cx),
8000 vec![
8001 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8002 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8003 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8004 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8005 ]
8006 );
8007 });
8008
8009 view.update(cx, |view, cx| {
8010 view.move_line_down(&MoveLineDown, cx);
8011 assert_eq!(
8012 view.display_text(cx),
8013 "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
8014 );
8015 assert_eq!(
8016 view.selections.display_ranges(cx),
8017 vec![
8018 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8019 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
8020 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
8021 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
8022 ]
8023 );
8024 });
8025
8026 view.update(cx, |view, cx| {
8027 view.move_line_up(&MoveLineUp, cx);
8028 assert_eq!(
8029 view.display_text(cx),
8030 "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
8031 );
8032 assert_eq!(
8033 view.selections.display_ranges(cx),
8034 vec![
8035 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
8036 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8037 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
8038 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
8039 ]
8040 );
8041 });
8042 }
8043
8044 #[gpui::test]
8045 fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
8046 cx.set_global(Settings::test(cx));
8047 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
8048 let snapshot = buffer.read(cx).snapshot(cx);
8049 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8050 editor.update(cx, |editor, cx| {
8051 editor.insert_blocks(
8052 [BlockProperties {
8053 style: BlockStyle::Fixed,
8054 position: snapshot.anchor_after(Point::new(2, 0)),
8055 disposition: BlockDisposition::Below,
8056 height: 1,
8057 render: Arc::new(|_| Empty::new().boxed()),
8058 }],
8059 cx,
8060 );
8061 editor.change_selections(None, cx, |s| {
8062 s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
8063 });
8064 editor.move_line_down(&MoveLineDown, cx);
8065 });
8066 }
8067
8068 #[gpui::test]
8069 fn test_transpose(cx: &mut gpui::MutableAppContext) {
8070 cx.set_global(Settings::test(cx));
8071
8072 cx.add_window(Default::default(), |cx| {
8073 let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
8074
8075 editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
8076 editor.transpose(&Default::default(), cx);
8077 assert_eq!(editor.text(cx), "bac");
8078 assert_eq!(editor.selections.ranges(cx), [2..2]);
8079
8080 editor.transpose(&Default::default(), cx);
8081 assert_eq!(editor.text(cx), "bca");
8082 assert_eq!(editor.selections.ranges(cx), [3..3]);
8083
8084 editor.transpose(&Default::default(), cx);
8085 assert_eq!(editor.text(cx), "bac");
8086 assert_eq!(editor.selections.ranges(cx), [3..3]);
8087
8088 editor
8089 })
8090 .1;
8091
8092 cx.add_window(Default::default(), |cx| {
8093 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8094
8095 editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
8096 editor.transpose(&Default::default(), cx);
8097 assert_eq!(editor.text(cx), "acb\nde");
8098 assert_eq!(editor.selections.ranges(cx), [3..3]);
8099
8100 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8101 editor.transpose(&Default::default(), cx);
8102 assert_eq!(editor.text(cx), "acbd\ne");
8103 assert_eq!(editor.selections.ranges(cx), [5..5]);
8104
8105 editor.transpose(&Default::default(), cx);
8106 assert_eq!(editor.text(cx), "acbde\n");
8107 assert_eq!(editor.selections.ranges(cx), [6..6]);
8108
8109 editor.transpose(&Default::default(), cx);
8110 assert_eq!(editor.text(cx), "acbd\ne");
8111 assert_eq!(editor.selections.ranges(cx), [6..6]);
8112
8113 editor
8114 })
8115 .1;
8116
8117 cx.add_window(Default::default(), |cx| {
8118 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8119
8120 editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
8121 editor.transpose(&Default::default(), cx);
8122 assert_eq!(editor.text(cx), "bacd\ne");
8123 assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
8124
8125 editor.transpose(&Default::default(), cx);
8126 assert_eq!(editor.text(cx), "bcade\n");
8127 assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
8128
8129 editor.transpose(&Default::default(), cx);
8130 assert_eq!(editor.text(cx), "bcda\ne");
8131 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8132
8133 editor.transpose(&Default::default(), cx);
8134 assert_eq!(editor.text(cx), "bcade\n");
8135 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8136
8137 editor.transpose(&Default::default(), cx);
8138 assert_eq!(editor.text(cx), "bcaed\n");
8139 assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
8140
8141 editor
8142 })
8143 .1;
8144
8145 cx.add_window(Default::default(), |cx| {
8146 let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
8147
8148 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8149 editor.transpose(&Default::default(), cx);
8150 assert_eq!(editor.text(cx), "🏀🍐✋");
8151 assert_eq!(editor.selections.ranges(cx), [8..8]);
8152
8153 editor.transpose(&Default::default(), cx);
8154 assert_eq!(editor.text(cx), "🏀✋🍐");
8155 assert_eq!(editor.selections.ranges(cx), [11..11]);
8156
8157 editor.transpose(&Default::default(), cx);
8158 assert_eq!(editor.text(cx), "🏀🍐✋");
8159 assert_eq!(editor.selections.ranges(cx), [11..11]);
8160
8161 editor
8162 })
8163 .1;
8164 }
8165
8166 #[gpui::test]
8167 async fn test_clipboard(cx: &mut gpui::TestAppContext) {
8168 let mut cx = EditorTestContext::new(cx).await;
8169
8170 cx.set_state("[one✅ }two [three }four [five }six ");
8171 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8172 cx.assert_editor_state("|two |four |six ");
8173
8174 // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
8175 cx.set_state("two |four |six |");
8176 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8177 cx.assert_editor_state("two one✅ |four three |six five |");
8178
8179 // Paste again but with only two cursors. Since the number of cursors doesn't
8180 // match the number of slices in the clipboard, the entire clipboard text
8181 // is pasted at each cursor.
8182 cx.set_state("|two one✅ four three six five |");
8183 cx.update_editor(|e, cx| {
8184 e.handle_input(&Input("( ".into()), cx);
8185 e.paste(&Paste, cx);
8186 e.handle_input(&Input(") ".into()), cx);
8187 });
8188 cx.assert_editor_state(indoc! {"
8189 ( one✅
8190 three
8191 five ) |two one✅ four three six five ( one✅
8192 three
8193 five ) |"});
8194
8195 // Cut with three selections, one of which is full-line.
8196 cx.set_state(indoc! {"
8197 1[2}3
8198 4|567
8199 [8}9"});
8200 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8201 cx.assert_editor_state(indoc! {"
8202 1|3
8203 |9"});
8204
8205 // Paste with three selections, noticing how the copied selection that was full-line
8206 // gets inserted before the second cursor.
8207 cx.set_state(indoc! {"
8208 1|3
8209 9|
8210 [o}ne"});
8211 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8212 cx.assert_editor_state(indoc! {"
8213 12|3
8214 4567
8215 9|
8216 8|ne"});
8217
8218 // Copy with a single cursor only, which writes the whole line into the clipboard.
8219 cx.set_state(indoc! {"
8220 The quick brown
8221 fox ju|mps over
8222 the lazy dog"});
8223 cx.update_editor(|e, cx| e.copy(&Copy, cx));
8224 cx.assert_clipboard_content(Some("fox jumps over\n"));
8225
8226 // Paste with three selections, noticing how the copied full-line selection is inserted
8227 // before the empty selections but replaces the selection that is non-empty.
8228 cx.set_state(indoc! {"
8229 T|he quick brown
8230 [fo}x jumps over
8231 t|he lazy dog"});
8232 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8233 cx.assert_editor_state(indoc! {"
8234 fox jumps over
8235 T|he quick brown
8236 fox jumps over
8237 |x jumps over
8238 fox jumps over
8239 t|he lazy dog"});
8240 }
8241
8242 #[gpui::test]
8243 fn test_select_all(cx: &mut gpui::MutableAppContext) {
8244 cx.set_global(Settings::test(cx));
8245 let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
8246 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8247 view.update(cx, |view, cx| {
8248 view.select_all(&SelectAll, cx);
8249 assert_eq!(
8250 view.selections.display_ranges(cx),
8251 &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
8252 );
8253 });
8254 }
8255
8256 #[gpui::test]
8257 fn test_select_line(cx: &mut gpui::MutableAppContext) {
8258 cx.set_global(Settings::test(cx));
8259 let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
8260 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8261 view.update(cx, |view, cx| {
8262 view.change_selections(None, cx, |s| {
8263 s.select_display_ranges([
8264 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8265 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8266 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8267 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
8268 ])
8269 });
8270 view.select_line(&SelectLine, cx);
8271 assert_eq!(
8272 view.selections.display_ranges(cx),
8273 vec![
8274 DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
8275 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
8276 ]
8277 );
8278 });
8279
8280 view.update(cx, |view, cx| {
8281 view.select_line(&SelectLine, cx);
8282 assert_eq!(
8283 view.selections.display_ranges(cx),
8284 vec![
8285 DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
8286 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
8287 ]
8288 );
8289 });
8290
8291 view.update(cx, |view, cx| {
8292 view.select_line(&SelectLine, cx);
8293 assert_eq!(
8294 view.selections.display_ranges(cx),
8295 vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
8296 );
8297 });
8298 }
8299
8300 #[gpui::test]
8301 fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
8302 cx.set_global(Settings::test(cx));
8303 let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
8304 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8305 view.update(cx, |view, cx| {
8306 view.fold_ranges(
8307 vec![
8308 Point::new(0, 2)..Point::new(1, 2),
8309 Point::new(2, 3)..Point::new(4, 1),
8310 Point::new(7, 0)..Point::new(8, 4),
8311 ],
8312 cx,
8313 );
8314 view.change_selections(None, cx, |s| {
8315 s.select_display_ranges([
8316 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8317 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8318 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8319 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
8320 ])
8321 });
8322 assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i");
8323 });
8324
8325 view.update(cx, |view, cx| {
8326 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8327 assert_eq!(
8328 view.display_text(cx),
8329 "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i"
8330 );
8331 assert_eq!(
8332 view.selections.display_ranges(cx),
8333 [
8334 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8335 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8336 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
8337 DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
8338 ]
8339 );
8340 });
8341
8342 view.update(cx, |view, cx| {
8343 view.change_selections(None, cx, |s| {
8344 s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
8345 });
8346 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8347 assert_eq!(
8348 view.display_text(cx),
8349 "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
8350 );
8351 assert_eq!(
8352 view.selections.display_ranges(cx),
8353 [
8354 DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
8355 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
8356 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
8357 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
8358 DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
8359 DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
8360 DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
8361 DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
8362 ]
8363 );
8364 });
8365 }
8366
8367 #[gpui::test]
8368 fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
8369 cx.set_global(Settings::test(cx));
8370 let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
8371 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8372
8373 view.update(cx, |view, cx| {
8374 view.change_selections(None, cx, |s| {
8375 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
8376 });
8377 });
8378 view.update(cx, |view, cx| {
8379 view.add_selection_above(&AddSelectionAbove, cx);
8380 assert_eq!(
8381 view.selections.display_ranges(cx),
8382 vec![
8383 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8384 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8385 ]
8386 );
8387 });
8388
8389 view.update(cx, |view, cx| {
8390 view.add_selection_above(&AddSelectionAbove, cx);
8391 assert_eq!(
8392 view.selections.display_ranges(cx),
8393 vec![
8394 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8395 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8396 ]
8397 );
8398 });
8399
8400 view.update(cx, |view, cx| {
8401 view.add_selection_below(&AddSelectionBelow, cx);
8402 assert_eq!(
8403 view.selections.display_ranges(cx),
8404 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8405 );
8406
8407 view.undo_selection(&UndoSelection, cx);
8408 assert_eq!(
8409 view.selections.display_ranges(cx),
8410 vec![
8411 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8412 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8413 ]
8414 );
8415
8416 view.redo_selection(&RedoSelection, cx);
8417 assert_eq!(
8418 view.selections.display_ranges(cx),
8419 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8420 );
8421 });
8422
8423 view.update(cx, |view, cx| {
8424 view.add_selection_below(&AddSelectionBelow, cx);
8425 assert_eq!(
8426 view.selections.display_ranges(cx),
8427 vec![
8428 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8429 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8430 ]
8431 );
8432 });
8433
8434 view.update(cx, |view, cx| {
8435 view.add_selection_below(&AddSelectionBelow, cx);
8436 assert_eq!(
8437 view.selections.display_ranges(cx),
8438 vec![
8439 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8440 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8441 ]
8442 );
8443 });
8444
8445 view.update(cx, |view, cx| {
8446 view.change_selections(None, cx, |s| {
8447 s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
8448 });
8449 });
8450 view.update(cx, |view, cx| {
8451 view.add_selection_below(&AddSelectionBelow, cx);
8452 assert_eq!(
8453 view.selections.display_ranges(cx),
8454 vec![
8455 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8456 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8457 ]
8458 );
8459 });
8460
8461 view.update(cx, |view, cx| {
8462 view.add_selection_below(&AddSelectionBelow, cx);
8463 assert_eq!(
8464 view.selections.display_ranges(cx),
8465 vec![
8466 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8467 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8468 ]
8469 );
8470 });
8471
8472 view.update(cx, |view, cx| {
8473 view.add_selection_above(&AddSelectionAbove, cx);
8474 assert_eq!(
8475 view.selections.display_ranges(cx),
8476 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
8477 );
8478 });
8479
8480 view.update(cx, |view, cx| {
8481 view.add_selection_above(&AddSelectionAbove, cx);
8482 assert_eq!(
8483 view.selections.display_ranges(cx),
8484 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
8485 );
8486 });
8487
8488 view.update(cx, |view, cx| {
8489 view.change_selections(None, cx, |s| {
8490 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
8491 });
8492 view.add_selection_below(&AddSelectionBelow, cx);
8493 assert_eq!(
8494 view.selections.display_ranges(cx),
8495 vec![
8496 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8497 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8498 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8499 ]
8500 );
8501 });
8502
8503 view.update(cx, |view, cx| {
8504 view.add_selection_below(&AddSelectionBelow, cx);
8505 assert_eq!(
8506 view.selections.display_ranges(cx),
8507 vec![
8508 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8509 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8510 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8511 DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
8512 ]
8513 );
8514 });
8515
8516 view.update(cx, |view, cx| {
8517 view.add_selection_above(&AddSelectionAbove, 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 ]
8525 );
8526 });
8527
8528 view.update(cx, |view, cx| {
8529 view.change_selections(None, cx, |s| {
8530 s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
8531 });
8532 });
8533 view.update(cx, |view, cx| {
8534 view.add_selection_above(&AddSelectionAbove, cx);
8535 assert_eq!(
8536 view.selections.display_ranges(cx),
8537 vec![
8538 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
8539 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
8540 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
8541 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
8542 ]
8543 );
8544 });
8545
8546 view.update(cx, |view, cx| {
8547 view.add_selection_below(&AddSelectionBelow, cx);
8548 assert_eq!(
8549 view.selections.display_ranges(cx),
8550 vec![
8551 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
8552 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
8553 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
8554 ]
8555 );
8556 });
8557 }
8558
8559 #[gpui::test]
8560 fn test_select_next(cx: &mut gpui::MutableAppContext) {
8561 cx.set_global(Settings::test(cx));
8562
8563 let (text, ranges) = marked_text_ranges("[abc]\n[abc] [abc]\ndefabc\n[abc]");
8564 let buffer = MultiBuffer::build_simple(&text, cx);
8565 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8566
8567 view.update(cx, |view, cx| {
8568 view.change_selections(None, cx, |s| {
8569 s.select_ranges([ranges[1].start + 1..ranges[1].start + 1])
8570 });
8571 view.select_next(
8572 &SelectNext {
8573 replace_newest: false,
8574 },
8575 cx,
8576 );
8577 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
8578
8579 view.select_next(
8580 &SelectNext {
8581 replace_newest: false,
8582 },
8583 cx,
8584 );
8585 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
8586
8587 view.undo_selection(&UndoSelection, cx);
8588 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
8589
8590 view.redo_selection(&RedoSelection, cx);
8591 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
8592
8593 view.select_next(
8594 &SelectNext {
8595 replace_newest: false,
8596 },
8597 cx,
8598 );
8599 assert_eq!(view.selections.ranges(cx), &ranges[1..4]);
8600
8601 view.select_next(
8602 &SelectNext {
8603 replace_newest: false,
8604 },
8605 cx,
8606 );
8607 assert_eq!(view.selections.ranges(cx), &ranges[0..4]);
8608 });
8609 }
8610
8611 #[gpui::test]
8612 async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
8613 cx.update(|cx| cx.set_global(Settings::test(cx)));
8614 let language = Arc::new(Language::new(
8615 LanguageConfig::default(),
8616 Some(tree_sitter_rust::language()),
8617 ));
8618
8619 let text = r#"
8620 use mod1::mod2::{mod3, mod4};
8621
8622 fn fn_1(param1: bool, param2: &str) {
8623 let var1 = "text";
8624 }
8625 "#
8626 .unindent();
8627
8628 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8629 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8630 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8631 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8632 .await;
8633
8634 view.update(cx, |view, cx| {
8635 view.change_selections(None, cx, |s| {
8636 s.select_display_ranges([
8637 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8638 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8639 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8640 ]);
8641 });
8642 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8643 });
8644 assert_eq!(
8645 view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
8646 &[
8647 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8648 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8649 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
8650 ]
8651 );
8652
8653 view.update(cx, |view, cx| {
8654 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8655 });
8656 assert_eq!(
8657 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8658 &[
8659 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8660 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8661 ]
8662 );
8663
8664 view.update(cx, |view, cx| {
8665 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8666 });
8667 assert_eq!(
8668 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8669 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8670 );
8671
8672 // Trying to expand the selected syntax node one more time has no effect.
8673 view.update(cx, |view, cx| {
8674 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8675 });
8676 assert_eq!(
8677 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8678 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8679 );
8680
8681 view.update(cx, |view, cx| {
8682 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8683 });
8684 assert_eq!(
8685 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8686 &[
8687 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8688 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8689 ]
8690 );
8691
8692 view.update(cx, |view, cx| {
8693 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8694 });
8695 assert_eq!(
8696 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8697 &[
8698 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8699 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8700 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
8701 ]
8702 );
8703
8704 view.update(cx, |view, cx| {
8705 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8706 });
8707 assert_eq!(
8708 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8709 &[
8710 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8711 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8712 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8713 ]
8714 );
8715
8716 // Trying to shrink the selected syntax node one more time has no effect.
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 // Ensure that we keep expanding the selection if the larger selection starts or ends within
8730 // a fold.
8731 view.update(cx, |view, cx| {
8732 view.fold_ranges(
8733 vec![
8734 Point::new(0, 21)..Point::new(0, 24),
8735 Point::new(3, 20)..Point::new(3, 22),
8736 ],
8737 cx,
8738 );
8739 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8740 });
8741 assert_eq!(
8742 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8743 &[
8744 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8745 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8746 DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
8747 ]
8748 );
8749 }
8750
8751 #[gpui::test]
8752 async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
8753 cx.update(|cx| cx.set_global(Settings::test(cx)));
8754 let language = Arc::new(
8755 Language::new(
8756 LanguageConfig {
8757 brackets: vec![
8758 BracketPair {
8759 start: "{".to_string(),
8760 end: "}".to_string(),
8761 close: false,
8762 newline: true,
8763 },
8764 BracketPair {
8765 start: "(".to_string(),
8766 end: ")".to_string(),
8767 close: false,
8768 newline: true,
8769 },
8770 ],
8771 ..Default::default()
8772 },
8773 Some(tree_sitter_rust::language()),
8774 )
8775 .with_indents_query(
8776 r#"
8777 (_ "(" ")" @end) @indent
8778 (_ "{" "}" @end) @indent
8779 "#,
8780 )
8781 .unwrap(),
8782 );
8783
8784 let text = "fn a() {}";
8785
8786 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8787 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8788 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
8789 editor
8790 .condition(&cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
8791 .await;
8792
8793 editor.update(cx, |editor, cx| {
8794 editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
8795 editor.newline(&Newline, cx);
8796 assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
8797 assert_eq!(
8798 editor.selections.ranges(cx),
8799 &[
8800 Point::new(1, 4)..Point::new(1, 4),
8801 Point::new(3, 4)..Point::new(3, 4),
8802 Point::new(5, 0)..Point::new(5, 0)
8803 ]
8804 );
8805 });
8806 }
8807
8808 #[gpui::test]
8809 async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
8810 cx.update(|cx| cx.set_global(Settings::test(cx)));
8811 let language = Arc::new(Language::new(
8812 LanguageConfig {
8813 brackets: vec![
8814 BracketPair {
8815 start: "{".to_string(),
8816 end: "}".to_string(),
8817 close: true,
8818 newline: true,
8819 },
8820 BracketPair {
8821 start: "/*".to_string(),
8822 end: " */".to_string(),
8823 close: true,
8824 newline: true,
8825 },
8826 BracketPair {
8827 start: "[".to_string(),
8828 end: "]".to_string(),
8829 close: false,
8830 newline: true,
8831 },
8832 ],
8833 autoclose_before: "})]".to_string(),
8834 ..Default::default()
8835 },
8836 Some(tree_sitter_rust::language()),
8837 ));
8838
8839 let text = r#"
8840 a
8841
8842 /
8843
8844 "#
8845 .unindent();
8846
8847 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8848 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8849 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8850 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8851 .await;
8852
8853 view.update(cx, |view, cx| {
8854 view.change_selections(None, cx, |s| {
8855 s.select_display_ranges([
8856 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8857 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8858 ])
8859 });
8860
8861 view.handle_input(&Input("{".to_string()), cx);
8862 view.handle_input(&Input("{".to_string()), cx);
8863 view.handle_input(&Input("{".to_string()), cx);
8864 assert_eq!(
8865 view.text(cx),
8866 "
8867 {{{}}}
8868 {{{}}}
8869 /
8870
8871 "
8872 .unindent()
8873 );
8874
8875 view.move_right(&MoveRight, cx);
8876 view.handle_input(&Input("}".to_string()), cx);
8877 view.handle_input(&Input("}".to_string()), cx);
8878 view.handle_input(&Input("}".to_string()), cx);
8879 assert_eq!(
8880 view.text(cx),
8881 "
8882 {{{}}}}
8883 {{{}}}}
8884 /
8885
8886 "
8887 .unindent()
8888 );
8889
8890 view.undo(&Undo, cx);
8891 view.handle_input(&Input("/".to_string()), cx);
8892 view.handle_input(&Input("*".to_string()), cx);
8893 assert_eq!(
8894 view.text(cx),
8895 "
8896 /* */
8897 /* */
8898 /
8899
8900 "
8901 .unindent()
8902 );
8903
8904 view.undo(&Undo, cx);
8905 view.change_selections(None, cx, |s| {
8906 s.select_display_ranges([
8907 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8908 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8909 ])
8910 });
8911 view.handle_input(&Input("*".to_string()), cx);
8912 assert_eq!(
8913 view.text(cx),
8914 "
8915 a
8916
8917 /*
8918 *
8919 "
8920 .unindent()
8921 );
8922
8923 // Don't autoclose if the next character isn't whitespace and isn't
8924 // listed in the language's "autoclose_before" section.
8925 view.finalize_last_transaction(cx);
8926 view.change_selections(None, cx, |s| {
8927 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)])
8928 });
8929 view.handle_input(&Input("{".to_string()), cx);
8930 assert_eq!(
8931 view.text(cx),
8932 "
8933 {a
8934
8935 /*
8936 *
8937 "
8938 .unindent()
8939 );
8940
8941 view.undo(&Undo, cx);
8942 view.change_selections(None, cx, |s| {
8943 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1)])
8944 });
8945 view.handle_input(&Input("{".to_string()), cx);
8946 assert_eq!(
8947 view.text(cx),
8948 "
8949 {a}
8950
8951 /*
8952 *
8953 "
8954 .unindent()
8955 );
8956 assert_eq!(
8957 view.selections.display_ranges(cx),
8958 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
8959 );
8960
8961 view.undo(&Undo, cx);
8962 view.handle_input(&Input("[".to_string()), cx);
8963 assert_eq!(
8964 view.text(cx),
8965 "
8966 [a]
8967
8968 /*
8969 *
8970 "
8971 .unindent()
8972 );
8973 assert_eq!(
8974 view.selections.display_ranges(cx),
8975 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
8976 );
8977
8978 view.undo(&Undo, cx);
8979 view.change_selections(None, cx, |s| {
8980 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)])
8981 });
8982 view.handle_input(&Input("[".to_string()), cx);
8983 assert_eq!(
8984 view.text(cx),
8985 "
8986 a[
8987
8988 /*
8989 *
8990 "
8991 .unindent()
8992 );
8993 assert_eq!(
8994 view.selections.display_ranges(cx),
8995 [DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2)]
8996 );
8997 });
8998 }
8999
9000 #[gpui::test]
9001 async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
9002 cx.update(|cx| cx.set_global(Settings::test(cx)));
9003 let language = Arc::new(Language::new(
9004 LanguageConfig {
9005 brackets: vec![BracketPair {
9006 start: "{".to_string(),
9007 end: "}".to_string(),
9008 close: true,
9009 newline: true,
9010 }],
9011 ..Default::default()
9012 },
9013 Some(tree_sitter_rust::language()),
9014 ));
9015
9016 let text = r#"
9017 a
9018 b
9019 c
9020 "#
9021 .unindent();
9022
9023 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9024 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9025 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9026 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9027 .await;
9028
9029 view.update(cx, |view, cx| {
9030 view.change_selections(None, cx, |s| {
9031 s.select_display_ranges([
9032 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9033 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
9034 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
9035 ])
9036 });
9037
9038 view.handle_input(&Input("{".to_string()), cx);
9039 view.handle_input(&Input("{".to_string()), cx);
9040 view.handle_input(&Input("{".to_string()), cx);
9041 assert_eq!(
9042 view.text(cx),
9043 "
9044 {{{a}}}
9045 {{{b}}}
9046 {{{c}}}
9047 "
9048 .unindent()
9049 );
9050 assert_eq!(
9051 view.selections.display_ranges(cx),
9052 [
9053 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
9054 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
9055 DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
9056 ]
9057 );
9058
9059 view.undo(&Undo, cx);
9060 assert_eq!(
9061 view.text(cx),
9062 "
9063 a
9064 b
9065 c
9066 "
9067 .unindent()
9068 );
9069 assert_eq!(
9070 view.selections.display_ranges(cx),
9071 [
9072 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9073 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
9074 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
9075 ]
9076 );
9077 });
9078 }
9079
9080 #[gpui::test]
9081 async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
9082 cx.update(|cx| cx.set_global(Settings::test(cx)));
9083 let language = Arc::new(Language::new(
9084 LanguageConfig {
9085 brackets: vec![BracketPair {
9086 start: "{".to_string(),
9087 end: "}".to_string(),
9088 close: true,
9089 newline: true,
9090 }],
9091 autoclose_before: "}".to_string(),
9092 ..Default::default()
9093 },
9094 Some(tree_sitter_rust::language()),
9095 ));
9096
9097 let text = r#"
9098 a
9099 b
9100 c
9101 "#
9102 .unindent();
9103
9104 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9105 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9106 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9107 editor
9108 .condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9109 .await;
9110
9111 editor.update(cx, |editor, cx| {
9112 editor.change_selections(None, cx, |s| {
9113 s.select_ranges([
9114 Point::new(0, 1)..Point::new(0, 1),
9115 Point::new(1, 1)..Point::new(1, 1),
9116 Point::new(2, 1)..Point::new(2, 1),
9117 ])
9118 });
9119
9120 editor.handle_input(&Input("{".to_string()), cx);
9121 editor.handle_input(&Input("{".to_string()), cx);
9122 editor.handle_input(&Input("_".to_string()), cx);
9123 assert_eq!(
9124 editor.text(cx),
9125 "
9126 a{{_}}
9127 b{{_}}
9128 c{{_}}
9129 "
9130 .unindent()
9131 );
9132 assert_eq!(
9133 editor.selections.ranges::<Point>(cx),
9134 [
9135 Point::new(0, 4)..Point::new(0, 4),
9136 Point::new(1, 4)..Point::new(1, 4),
9137 Point::new(2, 4)..Point::new(2, 4)
9138 ]
9139 );
9140
9141 editor.backspace(&Default::default(), cx);
9142 editor.backspace(&Default::default(), cx);
9143 assert_eq!(
9144 editor.text(cx),
9145 "
9146 a{}
9147 b{}
9148 c{}
9149 "
9150 .unindent()
9151 );
9152 assert_eq!(
9153 editor.selections.ranges::<Point>(cx),
9154 [
9155 Point::new(0, 2)..Point::new(0, 2),
9156 Point::new(1, 2)..Point::new(1, 2),
9157 Point::new(2, 2)..Point::new(2, 2)
9158 ]
9159 );
9160
9161 editor.delete_to_previous_word_start(&Default::default(), cx);
9162 assert_eq!(
9163 editor.text(cx),
9164 "
9165 a
9166 b
9167 c
9168 "
9169 .unindent()
9170 );
9171 assert_eq!(
9172 editor.selections.ranges::<Point>(cx),
9173 [
9174 Point::new(0, 1)..Point::new(0, 1),
9175 Point::new(1, 1)..Point::new(1, 1),
9176 Point::new(2, 1)..Point::new(2, 1)
9177 ]
9178 );
9179 });
9180 }
9181
9182 #[gpui::test]
9183 async fn test_snippets(cx: &mut gpui::TestAppContext) {
9184 cx.update(|cx| cx.set_global(Settings::test(cx)));
9185
9186 let (text, insertion_ranges) = marked_text_ranges(indoc! {"
9187 a.| b
9188 a.| b
9189 a.| b"});
9190 let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
9191 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9192
9193 editor.update(cx, |editor, cx| {
9194 let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
9195
9196 editor
9197 .insert_snippet(&insertion_ranges, snippet, cx)
9198 .unwrap();
9199
9200 fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text_ranges: &str) {
9201 let range_markers = ('<', '>');
9202 let (expected_text, mut selection_ranges_lookup) =
9203 marked_text_ranges_by(marked_text_ranges, vec![range_markers.clone().into()]);
9204 let selection_ranges = selection_ranges_lookup
9205 .remove(&range_markers.into())
9206 .unwrap();
9207 assert_eq!(editor.text(cx), expected_text);
9208 assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
9209 }
9210 assert(
9211 editor,
9212 cx,
9213 indoc! {"
9214 a.f(<one>, two, <three>) b
9215 a.f(<one>, two, <three>) b
9216 a.f(<one>, two, <three>) b"},
9217 );
9218
9219 // Can't move earlier than the first tab stop
9220 assert!(!editor.move_to_prev_snippet_tabstop(cx));
9221 assert(
9222 editor,
9223 cx,
9224 indoc! {"
9225 a.f(<one>, two, <three>) b
9226 a.f(<one>, two, <three>) b
9227 a.f(<one>, two, <three>) b"},
9228 );
9229
9230 assert!(editor.move_to_next_snippet_tabstop(cx));
9231 assert(
9232 editor,
9233 cx,
9234 indoc! {"
9235 a.f(one, <two>, three) b
9236 a.f(one, <two>, three) b
9237 a.f(one, <two>, three) b"},
9238 );
9239
9240 editor.move_to_prev_snippet_tabstop(cx);
9241 assert(
9242 editor,
9243 cx,
9244 indoc! {"
9245 a.f(<one>, two, <three>) b
9246 a.f(<one>, two, <three>) b
9247 a.f(<one>, two, <three>) b"},
9248 );
9249
9250 assert!(editor.move_to_next_snippet_tabstop(cx));
9251 assert(
9252 editor,
9253 cx,
9254 indoc! {"
9255 a.f(one, <two>, three) b
9256 a.f(one, <two>, three) b
9257 a.f(one, <two>, three) b"},
9258 );
9259 assert!(editor.move_to_next_snippet_tabstop(cx));
9260 assert(
9261 editor,
9262 cx,
9263 indoc! {"
9264 a.f(one, two, three)<> b
9265 a.f(one, two, three)<> b
9266 a.f(one, two, three)<> b"},
9267 );
9268
9269 // As soon as the last tab stop is reached, snippet state is gone
9270 editor.move_to_prev_snippet_tabstop(cx);
9271 assert(
9272 editor,
9273 cx,
9274 indoc! {"
9275 a.f(one, two, three)<> b
9276 a.f(one, two, three)<> b
9277 a.f(one, two, three)<> b"},
9278 );
9279 });
9280 }
9281
9282 #[gpui::test]
9283 async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
9284 cx.foreground().forbid_parking();
9285
9286 let mut language = Language::new(
9287 LanguageConfig {
9288 name: "Rust".into(),
9289 path_suffixes: vec!["rs".to_string()],
9290 ..Default::default()
9291 },
9292 Some(tree_sitter_rust::language()),
9293 );
9294 let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
9295 capabilities: lsp::ServerCapabilities {
9296 document_formatting_provider: Some(lsp::OneOf::Left(true)),
9297 ..Default::default()
9298 },
9299 ..Default::default()
9300 });
9301
9302 let fs = FakeFs::new(cx.background().clone());
9303 fs.insert_file("/file.rs", Default::default()).await;
9304
9305 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9306 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9307 let buffer = project
9308 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9309 .await
9310 .unwrap();
9311
9312 cx.foreground().start_waiting();
9313 let fake_server = fake_servers.next().await.unwrap();
9314
9315 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9316 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9317 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9318 assert!(cx.read(|cx| editor.is_dirty(cx)));
9319
9320 let save = cx.update(|cx| editor.save(project.clone(), cx));
9321 fake_server
9322 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9323 assert_eq!(
9324 params.text_document.uri,
9325 lsp::Url::from_file_path("/file.rs").unwrap()
9326 );
9327 assert_eq!(params.options.tab_size, 4);
9328 Ok(Some(vec![lsp::TextEdit::new(
9329 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
9330 ", ".to_string(),
9331 )]))
9332 })
9333 .next()
9334 .await;
9335 cx.foreground().start_waiting();
9336 save.await.unwrap();
9337 assert_eq!(
9338 editor.read_with(cx, |editor, cx| editor.text(cx)),
9339 "one, two\nthree\n"
9340 );
9341 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9342
9343 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9344 assert!(cx.read(|cx| editor.is_dirty(cx)));
9345
9346 // Ensure we can still save even if formatting hangs.
9347 fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9348 assert_eq!(
9349 params.text_document.uri,
9350 lsp::Url::from_file_path("/file.rs").unwrap()
9351 );
9352 futures::future::pending::<()>().await;
9353 unreachable!()
9354 });
9355 let save = cx.update(|cx| editor.save(project.clone(), cx));
9356 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
9357 cx.foreground().start_waiting();
9358 save.await.unwrap();
9359 assert_eq!(
9360 editor.read_with(cx, |editor, cx| editor.text(cx)),
9361 "one\ntwo\nthree\n"
9362 );
9363 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9364
9365 // Set rust language override and assert overriden tabsize is sent to language server
9366 cx.update(|cx| {
9367 cx.update_global::<Settings, _, _>(|settings, _| {
9368 settings.language_overrides.insert(
9369 "Rust".into(),
9370 LanguageSettings {
9371 tab_size: Some(8.try_into().unwrap()),
9372 ..Default::default()
9373 },
9374 );
9375 })
9376 });
9377
9378 let save = cx.update(|cx| editor.save(project.clone(), cx));
9379 fake_server
9380 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9381 assert_eq!(
9382 params.text_document.uri,
9383 lsp::Url::from_file_path("/file.rs").unwrap()
9384 );
9385 assert_eq!(params.options.tab_size, 8);
9386 Ok(Some(vec![]))
9387 })
9388 .next()
9389 .await;
9390 cx.foreground().start_waiting();
9391 save.await.unwrap();
9392 }
9393
9394 #[gpui::test]
9395 async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
9396 cx.foreground().forbid_parking();
9397
9398 let mut language = Language::new(
9399 LanguageConfig {
9400 name: "Rust".into(),
9401 path_suffixes: vec!["rs".to_string()],
9402 ..Default::default()
9403 },
9404 Some(tree_sitter_rust::language()),
9405 );
9406 let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
9407 capabilities: lsp::ServerCapabilities {
9408 document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
9409 ..Default::default()
9410 },
9411 ..Default::default()
9412 });
9413
9414 let fs = FakeFs::new(cx.background().clone());
9415 fs.insert_file("/file.rs", Default::default()).await;
9416
9417 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9418 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9419 let buffer = project
9420 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9421 .await
9422 .unwrap();
9423
9424 cx.foreground().start_waiting();
9425 let fake_server = fake_servers.next().await.unwrap();
9426
9427 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9428 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9429 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9430 assert!(cx.read(|cx| editor.is_dirty(cx)));
9431
9432 let save = cx.update(|cx| editor.save(project.clone(), cx));
9433 fake_server
9434 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
9435 assert_eq!(
9436 params.text_document.uri,
9437 lsp::Url::from_file_path("/file.rs").unwrap()
9438 );
9439 assert_eq!(params.options.tab_size, 4);
9440 Ok(Some(vec![lsp::TextEdit::new(
9441 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
9442 ", ".to_string(),
9443 )]))
9444 })
9445 .next()
9446 .await;
9447 cx.foreground().start_waiting();
9448 save.await.unwrap();
9449 assert_eq!(
9450 editor.read_with(cx, |editor, cx| editor.text(cx)),
9451 "one, two\nthree\n"
9452 );
9453 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9454
9455 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9456 assert!(cx.read(|cx| editor.is_dirty(cx)));
9457
9458 // Ensure we can still save even if formatting hangs.
9459 fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
9460 move |params, _| async move {
9461 assert_eq!(
9462 params.text_document.uri,
9463 lsp::Url::from_file_path("/file.rs").unwrap()
9464 );
9465 futures::future::pending::<()>().await;
9466 unreachable!()
9467 },
9468 );
9469 let save = cx.update(|cx| editor.save(project.clone(), cx));
9470 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
9471 cx.foreground().start_waiting();
9472 save.await.unwrap();
9473 assert_eq!(
9474 editor.read_with(cx, |editor, cx| editor.text(cx)),
9475 "one\ntwo\nthree\n"
9476 );
9477 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9478
9479 // Set rust language override and assert overriden tabsize is sent to language server
9480 cx.update(|cx| {
9481 cx.update_global::<Settings, _, _>(|settings, _| {
9482 settings.language_overrides.insert(
9483 "Rust".into(),
9484 LanguageSettings {
9485 tab_size: Some(8.try_into().unwrap()),
9486 ..Default::default()
9487 },
9488 );
9489 })
9490 });
9491
9492 let save = cx.update(|cx| editor.save(project.clone(), cx));
9493 fake_server
9494 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
9495 assert_eq!(
9496 params.text_document.uri,
9497 lsp::Url::from_file_path("/file.rs").unwrap()
9498 );
9499 assert_eq!(params.options.tab_size, 8);
9500 Ok(Some(vec![]))
9501 })
9502 .next()
9503 .await;
9504 cx.foreground().start_waiting();
9505 save.await.unwrap();
9506 }
9507
9508 #[gpui::test]
9509 async fn test_completion(cx: &mut gpui::TestAppContext) {
9510 let mut language = Language::new(
9511 LanguageConfig {
9512 name: "Rust".into(),
9513 path_suffixes: vec!["rs".to_string()],
9514 ..Default::default()
9515 },
9516 Some(tree_sitter_rust::language()),
9517 );
9518 let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
9519 capabilities: lsp::ServerCapabilities {
9520 completion_provider: Some(lsp::CompletionOptions {
9521 trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
9522 ..Default::default()
9523 }),
9524 ..Default::default()
9525 },
9526 ..Default::default()
9527 });
9528
9529 let text = "
9530 one
9531 two
9532 three
9533 "
9534 .unindent();
9535
9536 let fs = FakeFs::new(cx.background().clone());
9537 fs.insert_file("/file.rs", text).await;
9538
9539 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9540 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9541 let buffer = project
9542 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9543 .await
9544 .unwrap();
9545 let mut fake_server = fake_servers.next().await.unwrap();
9546
9547 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9548 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9549
9550 editor.update(cx, |editor, cx| {
9551 editor.project = Some(project);
9552 editor.change_selections(None, cx, |s| {
9553 s.select_ranges([Point::new(0, 3)..Point::new(0, 3)])
9554 });
9555 editor.handle_input(&Input(".".to_string()), cx);
9556 });
9557
9558 handle_completion_request(
9559 &mut fake_server,
9560 "/file.rs",
9561 Point::new(0, 4),
9562 vec![
9563 (Point::new(0, 4)..Point::new(0, 4), "first_completion"),
9564 (Point::new(0, 4)..Point::new(0, 4), "second_completion"),
9565 ],
9566 )
9567 .await;
9568 editor
9569 .condition(&cx, |editor, _| editor.context_menu_visible())
9570 .await;
9571
9572 let apply_additional_edits = editor.update(cx, |editor, cx| {
9573 editor.move_down(&MoveDown, cx);
9574 let apply_additional_edits = editor
9575 .confirm_completion(&ConfirmCompletion::default(), cx)
9576 .unwrap();
9577 assert_eq!(
9578 editor.text(cx),
9579 "
9580 one.second_completion
9581 two
9582 three
9583 "
9584 .unindent()
9585 );
9586 apply_additional_edits
9587 });
9588
9589 handle_resolve_completion_request(
9590 &mut fake_server,
9591 Some((Point::new(2, 5)..Point::new(2, 5), "\nadditional edit")),
9592 )
9593 .await;
9594 apply_additional_edits.await.unwrap();
9595 assert_eq!(
9596 editor.read_with(cx, |editor, cx| editor.text(cx)),
9597 "
9598 one.second_completion
9599 two
9600 three
9601 additional edit
9602 "
9603 .unindent()
9604 );
9605
9606 editor.update(cx, |editor, cx| {
9607 editor.change_selections(None, cx, |s| {
9608 s.select_ranges([
9609 Point::new(1, 3)..Point::new(1, 3),
9610 Point::new(2, 5)..Point::new(2, 5),
9611 ])
9612 });
9613
9614 editor.handle_input(&Input(" ".to_string()), cx);
9615 assert!(editor.context_menu.is_none());
9616 editor.handle_input(&Input("s".to_string()), cx);
9617 assert!(editor.context_menu.is_none());
9618 });
9619
9620 handle_completion_request(
9621 &mut fake_server,
9622 "/file.rs",
9623 Point::new(2, 7),
9624 vec![
9625 (Point::new(2, 6)..Point::new(2, 7), "fourth_completion"),
9626 (Point::new(2, 6)..Point::new(2, 7), "fifth_completion"),
9627 (Point::new(2, 6)..Point::new(2, 7), "sixth_completion"),
9628 ],
9629 )
9630 .await;
9631 editor
9632 .condition(&cx, |editor, _| editor.context_menu_visible())
9633 .await;
9634
9635 editor.update(cx, |editor, cx| {
9636 editor.handle_input(&Input("i".to_string()), cx);
9637 });
9638
9639 handle_completion_request(
9640 &mut fake_server,
9641 "/file.rs",
9642 Point::new(2, 8),
9643 vec![
9644 (Point::new(2, 6)..Point::new(2, 8), "fourth_completion"),
9645 (Point::new(2, 6)..Point::new(2, 8), "fifth_completion"),
9646 (Point::new(2, 6)..Point::new(2, 8), "sixth_completion"),
9647 ],
9648 )
9649 .await;
9650 editor
9651 .condition(&cx, |editor, _| editor.context_menu_visible())
9652 .await;
9653
9654 let apply_additional_edits = editor.update(cx, |editor, cx| {
9655 let apply_additional_edits = editor
9656 .confirm_completion(&ConfirmCompletion::default(), cx)
9657 .unwrap();
9658 assert_eq!(
9659 editor.text(cx),
9660 "
9661 one.second_completion
9662 two sixth_completion
9663 three sixth_completion
9664 additional edit
9665 "
9666 .unindent()
9667 );
9668 apply_additional_edits
9669 });
9670 handle_resolve_completion_request(&mut fake_server, None).await;
9671 apply_additional_edits.await.unwrap();
9672
9673 async fn handle_completion_request(
9674 fake: &mut FakeLanguageServer,
9675 path: &'static str,
9676 position: Point,
9677 completions: Vec<(Range<Point>, &'static str)>,
9678 ) {
9679 fake.handle_request::<lsp::request::Completion, _, _>(move |params, _| {
9680 let completions = completions.clone();
9681 async move {
9682 assert_eq!(
9683 params.text_document_position.text_document.uri,
9684 lsp::Url::from_file_path(path).unwrap()
9685 );
9686 assert_eq!(
9687 params.text_document_position.position,
9688 lsp::Position::new(position.row, position.column)
9689 );
9690 Ok(Some(lsp::CompletionResponse::Array(
9691 completions
9692 .iter()
9693 .map(|(range, new_text)| lsp::CompletionItem {
9694 label: new_text.to_string(),
9695 text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
9696 range: lsp::Range::new(
9697 lsp::Position::new(range.start.row, range.start.column),
9698 lsp::Position::new(range.start.row, range.start.column),
9699 ),
9700 new_text: new_text.to_string(),
9701 })),
9702 ..Default::default()
9703 })
9704 .collect(),
9705 )))
9706 }
9707 })
9708 .next()
9709 .await;
9710 }
9711
9712 async fn handle_resolve_completion_request(
9713 fake: &mut FakeLanguageServer,
9714 edit: Option<(Range<Point>, &'static str)>,
9715 ) {
9716 fake.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _| {
9717 let edit = edit.clone();
9718 async move {
9719 Ok(lsp::CompletionItem {
9720 additional_text_edits: edit.map(|(range, new_text)| {
9721 vec![lsp::TextEdit::new(
9722 lsp::Range::new(
9723 lsp::Position::new(range.start.row, range.start.column),
9724 lsp::Position::new(range.end.row, range.end.column),
9725 ),
9726 new_text.to_string(),
9727 )]
9728 }),
9729 ..Default::default()
9730 })
9731 }
9732 })
9733 .next()
9734 .await;
9735 }
9736 }
9737
9738 #[gpui::test]
9739 async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
9740 cx.update(|cx| cx.set_global(Settings::test(cx)));
9741 let language = Arc::new(Language::new(
9742 LanguageConfig {
9743 line_comment: Some("// ".to_string()),
9744 ..Default::default()
9745 },
9746 Some(tree_sitter_rust::language()),
9747 ));
9748
9749 let text = "
9750 fn a() {
9751 //b();
9752 // c();
9753 // d();
9754 }
9755 "
9756 .unindent();
9757
9758 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9759 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9760 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9761
9762 view.update(cx, |editor, cx| {
9763 // If multiple selections intersect a line, the line is only
9764 // toggled once.
9765 editor.change_selections(None, cx, |s| {
9766 s.select_display_ranges([
9767 DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
9768 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
9769 ])
9770 });
9771 editor.toggle_comments(&ToggleComments, cx);
9772 assert_eq!(
9773 editor.text(cx),
9774 "
9775 fn a() {
9776 b();
9777 c();
9778 d();
9779 }
9780 "
9781 .unindent()
9782 );
9783
9784 // The comment prefix is inserted at the same column for every line
9785 // in a selection.
9786 editor.change_selections(None, cx, |s| {
9787 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
9788 });
9789 editor.toggle_comments(&ToggleComments, cx);
9790 assert_eq!(
9791 editor.text(cx),
9792 "
9793 fn a() {
9794 // b();
9795 // c();
9796 // d();
9797 }
9798 "
9799 .unindent()
9800 );
9801
9802 // If a selection ends at the beginning of a line, that line is not toggled.
9803 editor.change_selections(None, cx, |s| {
9804 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
9805 });
9806 editor.toggle_comments(&ToggleComments, cx);
9807 assert_eq!(
9808 editor.text(cx),
9809 "
9810 fn a() {
9811 // b();
9812 c();
9813 // d();
9814 }
9815 "
9816 .unindent()
9817 );
9818 });
9819 }
9820
9821 #[gpui::test]
9822 fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
9823 cx.set_global(Settings::test(cx));
9824 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9825 let multibuffer = cx.add_model(|cx| {
9826 let mut multibuffer = MultiBuffer::new(0);
9827 multibuffer.push_excerpts(
9828 buffer.clone(),
9829 [
9830 ExcerptRange {
9831 context: Point::new(0, 0)..Point::new(0, 4),
9832 primary: None,
9833 },
9834 ExcerptRange {
9835 context: Point::new(1, 0)..Point::new(1, 4),
9836 primary: None,
9837 },
9838 ],
9839 cx,
9840 );
9841 multibuffer
9842 });
9843
9844 assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
9845
9846 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
9847 view.update(cx, |view, cx| {
9848 assert_eq!(view.text(cx), "aaaa\nbbbb");
9849 view.change_selections(None, cx, |s| {
9850 s.select_ranges([
9851 Point::new(0, 0)..Point::new(0, 0),
9852 Point::new(1, 0)..Point::new(1, 0),
9853 ])
9854 });
9855
9856 view.handle_input(&Input("X".to_string()), cx);
9857 assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
9858 assert_eq!(
9859 view.selections.ranges(cx),
9860 [
9861 Point::new(0, 1)..Point::new(0, 1),
9862 Point::new(1, 1)..Point::new(1, 1),
9863 ]
9864 )
9865 });
9866 }
9867
9868 #[gpui::test]
9869 fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
9870 cx.set_global(Settings::test(cx));
9871 let (initial_text, excerpt_ranges) = marked_text_ranges(indoc! {"
9872 [aaaa
9873 (bbbb]
9874 cccc)"});
9875 let excerpt_ranges = excerpt_ranges.into_iter().map(|context| ExcerptRange {
9876 context,
9877 primary: None,
9878 });
9879 let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
9880 let multibuffer = cx.add_model(|cx| {
9881 let mut multibuffer = MultiBuffer::new(0);
9882 multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
9883 multibuffer
9884 });
9885
9886 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
9887 view.update(cx, |view, cx| {
9888 let (expected_text, selection_ranges) = marked_text_ranges(indoc! {"
9889 aaaa
9890 b|bbb
9891 b|bb|b
9892 cccc"});
9893 assert_eq!(view.text(cx), expected_text);
9894 view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
9895
9896 view.handle_input(&Input("X".to_string()), cx);
9897
9898 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
9899 aaaa
9900 bX|bbXb
9901 bX|bbX|b
9902 cccc"});
9903 assert_eq!(view.text(cx), expected_text);
9904 assert_eq!(view.selections.ranges(cx), expected_selections);
9905
9906 view.newline(&Newline, cx);
9907 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
9908 aaaa
9909 bX
9910 |bbX
9911 b
9912 bX
9913 |bbX
9914 |b
9915 cccc"});
9916 assert_eq!(view.text(cx), expected_text);
9917 assert_eq!(view.selections.ranges(cx), expected_selections);
9918 });
9919 }
9920
9921 #[gpui::test]
9922 fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
9923 cx.set_global(Settings::test(cx));
9924 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9925 let mut excerpt1_id = None;
9926 let multibuffer = cx.add_model(|cx| {
9927 let mut multibuffer = MultiBuffer::new(0);
9928 excerpt1_id = multibuffer
9929 .push_excerpts(
9930 buffer.clone(),
9931 [
9932 ExcerptRange {
9933 context: Point::new(0, 0)..Point::new(1, 4),
9934 primary: None,
9935 },
9936 ExcerptRange {
9937 context: Point::new(1, 0)..Point::new(2, 4),
9938 primary: None,
9939 },
9940 ],
9941 cx,
9942 )
9943 .into_iter()
9944 .next();
9945 multibuffer
9946 });
9947 assert_eq!(
9948 multibuffer.read(cx).read(cx).text(),
9949 "aaaa\nbbbb\nbbbb\ncccc"
9950 );
9951 let (_, editor) = cx.add_window(Default::default(), |cx| {
9952 let mut editor = build_editor(multibuffer.clone(), cx);
9953 let snapshot = editor.snapshot(cx);
9954 editor.change_selections(None, cx, |s| {
9955 s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
9956 });
9957 editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
9958 assert_eq!(
9959 editor.selections.ranges(cx),
9960 [
9961 Point::new(1, 3)..Point::new(1, 3),
9962 Point::new(2, 1)..Point::new(2, 1),
9963 ]
9964 );
9965 editor
9966 });
9967
9968 // Refreshing selections is a no-op when excerpts haven't changed.
9969 editor.update(cx, |editor, cx| {
9970 editor.change_selections(None, cx, |s| {
9971 s.refresh();
9972 });
9973 assert_eq!(
9974 editor.selections.ranges(cx),
9975 [
9976 Point::new(1, 3)..Point::new(1, 3),
9977 Point::new(2, 1)..Point::new(2, 1),
9978 ]
9979 );
9980 });
9981
9982 multibuffer.update(cx, |multibuffer, cx| {
9983 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
9984 });
9985 editor.update(cx, |editor, cx| {
9986 // Removing an excerpt causes the first selection to become degenerate.
9987 assert_eq!(
9988 editor.selections.ranges(cx),
9989 [
9990 Point::new(0, 0)..Point::new(0, 0),
9991 Point::new(0, 1)..Point::new(0, 1)
9992 ]
9993 );
9994
9995 // Refreshing selections will relocate the first selection to the original buffer
9996 // location.
9997 editor.change_selections(None, cx, |s| {
9998 s.refresh();
9999 });
10000 assert_eq!(
10001 editor.selections.ranges(cx),
10002 [
10003 Point::new(0, 1)..Point::new(0, 1),
10004 Point::new(0, 3)..Point::new(0, 3)
10005 ]
10006 );
10007 assert!(editor.selections.pending_anchor().is_some());
10008 });
10009 }
10010
10011 #[gpui::test]
10012 fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
10013 cx.set_global(Settings::test(cx));
10014 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
10015 let mut excerpt1_id = None;
10016 let multibuffer = cx.add_model(|cx| {
10017 let mut multibuffer = MultiBuffer::new(0);
10018 excerpt1_id = multibuffer
10019 .push_excerpts(
10020 buffer.clone(),
10021 [
10022 ExcerptRange {
10023 context: Point::new(0, 0)..Point::new(1, 4),
10024 primary: None,
10025 },
10026 ExcerptRange {
10027 context: Point::new(1, 0)..Point::new(2, 4),
10028 primary: None,
10029 },
10030 ],
10031 cx,
10032 )
10033 .into_iter()
10034 .next();
10035 multibuffer
10036 });
10037 assert_eq!(
10038 multibuffer.read(cx).read(cx).text(),
10039 "aaaa\nbbbb\nbbbb\ncccc"
10040 );
10041 let (_, editor) = cx.add_window(Default::default(), |cx| {
10042 let mut editor = build_editor(multibuffer.clone(), cx);
10043 let snapshot = editor.snapshot(cx);
10044 editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
10045 assert_eq!(
10046 editor.selections.ranges(cx),
10047 [Point::new(1, 3)..Point::new(1, 3)]
10048 );
10049 editor
10050 });
10051
10052 multibuffer.update(cx, |multibuffer, cx| {
10053 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
10054 });
10055 editor.update(cx, |editor, cx| {
10056 assert_eq!(
10057 editor.selections.ranges(cx),
10058 [Point::new(0, 0)..Point::new(0, 0)]
10059 );
10060
10061 // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
10062 editor.change_selections(None, cx, |s| {
10063 s.refresh();
10064 });
10065 assert_eq!(
10066 editor.selections.ranges(cx),
10067 [Point::new(0, 3)..Point::new(0, 3)]
10068 );
10069 assert!(editor.selections.pending_anchor().is_some());
10070 });
10071 }
10072
10073 #[gpui::test]
10074 async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
10075 cx.update(|cx| cx.set_global(Settings::test(cx)));
10076 let language = Arc::new(
10077 Language::new(
10078 LanguageConfig {
10079 brackets: vec![
10080 BracketPair {
10081 start: "{".to_string(),
10082 end: "}".to_string(),
10083 close: true,
10084 newline: true,
10085 },
10086 BracketPair {
10087 start: "/* ".to_string(),
10088 end: " */".to_string(),
10089 close: true,
10090 newline: true,
10091 },
10092 ],
10093 ..Default::default()
10094 },
10095 Some(tree_sitter_rust::language()),
10096 )
10097 .with_indents_query("")
10098 .unwrap(),
10099 );
10100
10101 let text = concat!(
10102 "{ }\n", // Suppress rustfmt
10103 " x\n", //
10104 " /* */\n", //
10105 "x\n", //
10106 "{{} }\n", //
10107 );
10108
10109 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
10110 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
10111 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
10112 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
10113 .await;
10114
10115 view.update(cx, |view, cx| {
10116 view.change_selections(None, cx, |s| {
10117 s.select_display_ranges([
10118 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
10119 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
10120 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
10121 ])
10122 });
10123 view.newline(&Newline, cx);
10124
10125 assert_eq!(
10126 view.buffer().read(cx).read(cx).text(),
10127 concat!(
10128 "{ \n", // Suppress rustfmt
10129 "\n", //
10130 "}\n", //
10131 " x\n", //
10132 " /* \n", //
10133 " \n", //
10134 " */\n", //
10135 "x\n", //
10136 "{{} \n", //
10137 "}\n", //
10138 )
10139 );
10140 });
10141 }
10142
10143 #[gpui::test]
10144 fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
10145 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10146
10147 cx.set_global(Settings::test(cx));
10148 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
10149
10150 editor.update(cx, |editor, cx| {
10151 struct Type1;
10152 struct Type2;
10153
10154 let buffer = buffer.read(cx).snapshot(cx);
10155
10156 let anchor_range = |range: Range<Point>| {
10157 buffer.anchor_after(range.start)..buffer.anchor_after(range.end)
10158 };
10159
10160 editor.highlight_background::<Type1>(
10161 vec![
10162 anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10163 anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10164 anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10165 anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10166 ],
10167 |_| Color::red(),
10168 cx,
10169 );
10170 editor.highlight_background::<Type2>(
10171 vec![
10172 anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10173 anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10174 anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10175 anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10176 ],
10177 |_| Color::green(),
10178 cx,
10179 );
10180
10181 let snapshot = editor.snapshot(cx);
10182 let mut highlighted_ranges = editor.background_highlights_in_range(
10183 anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10184 &snapshot,
10185 cx.global::<Settings>().theme.as_ref(),
10186 );
10187 // Enforce a consistent ordering based on color without relying on the ordering of the
10188 // highlight's `TypeId` which is non-deterministic.
10189 highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10190 assert_eq!(
10191 highlighted_ranges,
10192 &[
10193 (
10194 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
10195 Color::green(),
10196 ),
10197 (
10198 DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
10199 Color::green(),
10200 ),
10201 (
10202 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
10203 Color::red(),
10204 ),
10205 (
10206 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
10207 Color::red(),
10208 ),
10209 ]
10210 );
10211 assert_eq!(
10212 editor.background_highlights_in_range(
10213 anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10214 &snapshot,
10215 cx.global::<Settings>().theme.as_ref(),
10216 ),
10217 &[(
10218 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
10219 Color::red(),
10220 )]
10221 );
10222 });
10223 }
10224
10225 #[gpui::test]
10226 fn test_following(cx: &mut gpui::MutableAppContext) {
10227 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10228
10229 cx.set_global(Settings::test(cx));
10230
10231 let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
10232 let (_, follower) = cx.add_window(
10233 WindowOptions {
10234 bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
10235 ..Default::default()
10236 },
10237 |cx| build_editor(buffer.clone(), cx),
10238 );
10239
10240 let pending_update = Rc::new(RefCell::new(None));
10241 follower.update(cx, {
10242 let update = pending_update.clone();
10243 |_, cx| {
10244 cx.subscribe(&leader, move |_, leader, event, cx| {
10245 leader
10246 .read(cx)
10247 .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
10248 })
10249 .detach();
10250 }
10251 });
10252
10253 // Update the selections only
10254 leader.update(cx, |leader, cx| {
10255 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
10256 });
10257 follower.update(cx, |follower, cx| {
10258 follower
10259 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10260 .unwrap();
10261 });
10262 assert_eq!(follower.read(cx).selections.ranges(cx), vec![1..1]);
10263
10264 // Update the scroll position only
10265 leader.update(cx, |leader, cx| {
10266 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
10267 });
10268 follower.update(cx, |follower, cx| {
10269 follower
10270 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10271 .unwrap();
10272 });
10273 assert_eq!(
10274 follower.update(cx, |follower, cx| follower.scroll_position(cx)),
10275 vec2f(1.5, 3.5)
10276 );
10277
10278 // Update the selections and scroll position
10279 leader.update(cx, |leader, cx| {
10280 leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
10281 leader.request_autoscroll(Autoscroll::Newest, cx);
10282 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
10283 });
10284 follower.update(cx, |follower, cx| {
10285 let initial_scroll_position = follower.scroll_position(cx);
10286 follower
10287 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10288 .unwrap();
10289 assert_eq!(follower.scroll_position(cx), initial_scroll_position);
10290 assert!(follower.autoscroll_request.is_some());
10291 });
10292 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0]);
10293
10294 // Creating a pending selection that precedes another selection
10295 leader.update(cx, |leader, cx| {
10296 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
10297 leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
10298 });
10299 follower.update(cx, |follower, cx| {
10300 follower
10301 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10302 .unwrap();
10303 });
10304 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0, 1..1]);
10305
10306 // Extend the pending selection so that it surrounds another selection
10307 leader.update(cx, |leader, cx| {
10308 leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
10309 });
10310 follower.update(cx, |follower, cx| {
10311 follower
10312 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10313 .unwrap();
10314 });
10315 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..2]);
10316 }
10317
10318 #[test]
10319 fn test_combine_syntax_and_fuzzy_match_highlights() {
10320 let string = "abcdefghijklmnop";
10321 let syntax_ranges = [
10322 (
10323 0..3,
10324 HighlightStyle {
10325 color: Some(Color::red()),
10326 ..Default::default()
10327 },
10328 ),
10329 (
10330 4..8,
10331 HighlightStyle {
10332 color: Some(Color::green()),
10333 ..Default::default()
10334 },
10335 ),
10336 ];
10337 let match_indices = [4, 6, 7, 8];
10338 assert_eq!(
10339 combine_syntax_and_fuzzy_match_highlights(
10340 &string,
10341 Default::default(),
10342 syntax_ranges.into_iter(),
10343 &match_indices,
10344 ),
10345 &[
10346 (
10347 0..3,
10348 HighlightStyle {
10349 color: Some(Color::red()),
10350 ..Default::default()
10351 },
10352 ),
10353 (
10354 4..5,
10355 HighlightStyle {
10356 color: Some(Color::green()),
10357 weight: Some(fonts::Weight::BOLD),
10358 ..Default::default()
10359 },
10360 ),
10361 (
10362 5..6,
10363 HighlightStyle {
10364 color: Some(Color::green()),
10365 ..Default::default()
10366 },
10367 ),
10368 (
10369 6..8,
10370 HighlightStyle {
10371 color: Some(Color::green()),
10372 weight: Some(fonts::Weight::BOLD),
10373 ..Default::default()
10374 },
10375 ),
10376 (
10377 8..9,
10378 HighlightStyle {
10379 weight: Some(fonts::Weight::BOLD),
10380 ..Default::default()
10381 },
10382 ),
10383 ]
10384 );
10385 }
10386
10387 fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
10388 let point = DisplayPoint::new(row as u32, column as u32);
10389 point..point
10390 }
10391
10392 fn assert_selection_ranges(
10393 marked_text: &str,
10394 selection_marker_pairs: Vec<(char, char)>,
10395 view: &mut Editor,
10396 cx: &mut ViewContext<Editor>,
10397 ) {
10398 let snapshot = view.snapshot(cx).display_snapshot;
10399 let mut marker_chars = Vec::new();
10400 for (start, end) in selection_marker_pairs.iter() {
10401 marker_chars.push(*start);
10402 marker_chars.push(*end);
10403 }
10404 let (_, markers) = marked_text_by(marked_text, marker_chars);
10405 let asserted_ranges: Vec<Range<DisplayPoint>> = selection_marker_pairs
10406 .iter()
10407 .map(|(start, end)| {
10408 let start = markers.get(start).unwrap()[0].to_display_point(&snapshot);
10409 let end = markers.get(end).unwrap()[0].to_display_point(&snapshot);
10410 start..end
10411 })
10412 .collect();
10413 assert_eq!(
10414 view.selections.display_ranges(cx),
10415 &asserted_ranges[..],
10416 "Assert selections are {}",
10417 marked_text
10418 );
10419 }
10420}
10421
10422trait RangeExt<T> {
10423 fn sorted(&self) -> Range<T>;
10424 fn to_inclusive(&self) -> RangeInclusive<T>;
10425}
10426
10427impl<T: Ord + Clone> RangeExt<T> for Range<T> {
10428 fn sorted(&self) -> Self {
10429 cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone()
10430 }
10431
10432 fn to_inclusive(&self) -> RangeInclusive<T> {
10433 self.start.clone()..=self.end.clone()
10434 }
10435}
10436
10437trait RangeToAnchorExt {
10438 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor>;
10439}
10440
10441impl<T: ToOffset> RangeToAnchorExt for Range<T> {
10442 fn to_anchors(self, snapshot: &MultiBufferSnapshot) -> Range<Anchor> {
10443 snapshot.anchor_after(self.start)..snapshot.anchor_before(self.end)
10444 }
10445}