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