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