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