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