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