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::{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 let nav_history = workspace.active_pane().read(cx).nav_history().clone();
4606 for definition in definitions {
4607 let range = definition
4608 .target
4609 .range
4610 .to_offset(definition.target.buffer.read(cx));
4611
4612 let target_editor_handle =
4613 workspace.open_project_item(definition.target.buffer, cx);
4614 target_editor_handle.update(cx, |target_editor, cx| {
4615 // When selecting a definition in a different buffer, disable the nav history
4616 // to avoid creating a history entry at the previous cursor location.
4617 if editor_handle != target_editor_handle {
4618 nav_history.borrow_mut().disable();
4619 }
4620 target_editor.change_selections(Some(Autoscroll::Center), cx, |s| {
4621 s.select_ranges([range]);
4622 });
4623
4624 nav_history.borrow_mut().enable();
4625 });
4626 }
4627 });
4628
4629 Ok::<(), anyhow::Error>(())
4630 })
4631 .detach_and_log_err(cx);
4632 }
4633
4634 pub fn find_all_references(
4635 workspace: &mut Workspace,
4636 _: &FindAllReferences,
4637 cx: &mut ViewContext<Workspace>,
4638 ) -> Option<Task<Result<()>>> {
4639 let active_item = workspace.active_item(cx)?;
4640 let editor_handle = active_item.act_as::<Self>(cx)?;
4641
4642 let editor = editor_handle.read(cx);
4643 let buffer = editor.buffer.read(cx);
4644 let head = editor.selections.newest::<usize>(cx).head();
4645 let (buffer, head) = buffer.text_anchor_for_position(head, cx)?;
4646 let replica_id = editor.replica_id(cx);
4647
4648 let project = workspace.project().clone();
4649 let references = project.update(cx, |project, cx| project.references(&buffer, head, cx));
4650 Some(cx.spawn(|workspace, mut cx| async move {
4651 let mut locations = references.await?;
4652 if locations.is_empty() {
4653 return Ok(());
4654 }
4655
4656 locations.sort_by_key(|location| location.buffer.id());
4657 let mut locations = locations.into_iter().peekable();
4658 let mut ranges_to_highlight = Vec::new();
4659
4660 let excerpt_buffer = cx.add_model(|cx| {
4661 let mut symbol_name = None;
4662 let mut multibuffer = MultiBuffer::new(replica_id);
4663 while let Some(location) = locations.next() {
4664 let buffer = location.buffer.read(cx);
4665 let mut ranges_for_buffer = Vec::new();
4666 let range = location.range.to_offset(buffer);
4667 ranges_for_buffer.push(range.clone());
4668 if symbol_name.is_none() {
4669 symbol_name = Some(buffer.text_for_range(range).collect::<String>());
4670 }
4671
4672 while let Some(next_location) = locations.peek() {
4673 if next_location.buffer == location.buffer {
4674 ranges_for_buffer.push(next_location.range.to_offset(buffer));
4675 locations.next();
4676 } else {
4677 break;
4678 }
4679 }
4680
4681 ranges_for_buffer.sort_by_key(|range| (range.start, Reverse(range.end)));
4682 ranges_to_highlight.extend(multibuffer.push_excerpts_with_context_lines(
4683 location.buffer.clone(),
4684 ranges_for_buffer,
4685 1,
4686 cx,
4687 ));
4688 }
4689 multibuffer.with_title(format!("References to `{}`", symbol_name.unwrap()))
4690 });
4691
4692 workspace.update(&mut cx, |workspace, cx| {
4693 let editor =
4694 cx.add_view(|cx| Editor::for_multibuffer(excerpt_buffer, Some(project), cx));
4695 editor.update(cx, |editor, cx| {
4696 editor.highlight_background::<Self>(
4697 ranges_to_highlight,
4698 |theme| theme.editor.highlighted_line_background,
4699 cx,
4700 );
4701 });
4702 workspace.add_item(Box::new(editor), cx);
4703 });
4704
4705 Ok(())
4706 }))
4707 }
4708
4709 pub fn rename(&mut self, _: &Rename, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
4710 use language::ToOffset as _;
4711
4712 let project = self.project.clone()?;
4713 let selection = self.selections.newest_anchor().clone();
4714 let (cursor_buffer, cursor_buffer_position) = self
4715 .buffer
4716 .read(cx)
4717 .text_anchor_for_position(selection.head(), cx)?;
4718 let (tail_buffer, _) = self
4719 .buffer
4720 .read(cx)
4721 .text_anchor_for_position(selection.tail(), cx)?;
4722 if tail_buffer != cursor_buffer {
4723 return None;
4724 }
4725
4726 let snapshot = cursor_buffer.read(cx).snapshot();
4727 let cursor_buffer_offset = cursor_buffer_position.to_offset(&snapshot);
4728 let prepare_rename = project.update(cx, |project, cx| {
4729 project.prepare_rename(cursor_buffer, cursor_buffer_offset, cx)
4730 });
4731
4732 Some(cx.spawn(|this, mut cx| async move {
4733 let rename_range = if let Some(range) = prepare_rename.await? {
4734 Some(range)
4735 } else {
4736 this.read_with(&cx, |this, cx| {
4737 let buffer = this.buffer.read(cx).snapshot(cx);
4738 let mut buffer_highlights = this
4739 .document_highlights_for_position(selection.head(), &buffer)
4740 .filter(|highlight| {
4741 highlight.start.excerpt_id() == selection.head().excerpt_id()
4742 && highlight.end.excerpt_id() == selection.head().excerpt_id()
4743 });
4744 buffer_highlights
4745 .next()
4746 .map(|highlight| highlight.start.text_anchor..highlight.end.text_anchor)
4747 })
4748 };
4749 if let Some(rename_range) = rename_range {
4750 let rename_buffer_range = rename_range.to_offset(&snapshot);
4751 let cursor_offset_in_rename_range =
4752 cursor_buffer_offset.saturating_sub(rename_buffer_range.start);
4753
4754 this.update(&mut cx, |this, cx| {
4755 this.take_rename(false, cx);
4756 let style = this.style(cx);
4757 let buffer = this.buffer.read(cx).read(cx);
4758 let cursor_offset = selection.head().to_offset(&buffer);
4759 let rename_start = cursor_offset.saturating_sub(cursor_offset_in_rename_range);
4760 let rename_end = rename_start + rename_buffer_range.len();
4761 let range = buffer.anchor_before(rename_start)..buffer.anchor_after(rename_end);
4762 let mut old_highlight_id = None;
4763 let old_name: Arc<str> = buffer
4764 .chunks(rename_start..rename_end, true)
4765 .map(|chunk| {
4766 if old_highlight_id.is_none() {
4767 old_highlight_id = chunk.syntax_highlight_id;
4768 }
4769 chunk.text
4770 })
4771 .collect::<String>()
4772 .into();
4773
4774 drop(buffer);
4775
4776 // Position the selection in the rename editor so that it matches the current selection.
4777 this.show_local_selections = false;
4778 let rename_editor = cx.add_view(|cx| {
4779 let mut editor = Editor::single_line(None, cx);
4780 if let Some(old_highlight_id) = old_highlight_id {
4781 editor.override_text_style =
4782 Some(Box::new(move |style| old_highlight_id.style(&style.syntax)));
4783 }
4784 editor
4785 .buffer
4786 .update(cx, |buffer, cx| buffer.edit([(0..0, old_name.clone())], cx));
4787 editor.select_all(&SelectAll, cx);
4788 editor
4789 });
4790
4791 let ranges = this
4792 .clear_background_highlights::<DocumentHighlightWrite>(cx)
4793 .into_iter()
4794 .flat_map(|(_, ranges)| ranges)
4795 .chain(
4796 this.clear_background_highlights::<DocumentHighlightRead>(cx)
4797 .into_iter()
4798 .flat_map(|(_, ranges)| ranges),
4799 )
4800 .collect();
4801
4802 this.highlight_text::<Rename>(
4803 ranges,
4804 HighlightStyle {
4805 fade_out: Some(style.rename_fade),
4806 ..Default::default()
4807 },
4808 cx,
4809 );
4810 cx.focus(&rename_editor);
4811 let block_id = this.insert_blocks(
4812 [BlockProperties {
4813 style: BlockStyle::Flex,
4814 position: range.start.clone(),
4815 height: 1,
4816 render: Arc::new({
4817 let editor = rename_editor.clone();
4818 move |cx: &mut BlockContext| {
4819 ChildView::new(editor.clone())
4820 .contained()
4821 .with_padding_left(cx.anchor_x)
4822 .boxed()
4823 }
4824 }),
4825 disposition: BlockDisposition::Below,
4826 }],
4827 cx,
4828 )[0];
4829 this.pending_rename = Some(RenameState {
4830 range,
4831 old_name,
4832 editor: rename_editor,
4833 block_id,
4834 });
4835 });
4836 }
4837
4838 Ok(())
4839 }))
4840 }
4841
4842 pub fn confirm_rename(
4843 workspace: &mut Workspace,
4844 _: &ConfirmRename,
4845 cx: &mut ViewContext<Workspace>,
4846 ) -> Option<Task<Result<()>>> {
4847 let editor = workspace.active_item(cx)?.act_as::<Editor>(cx)?;
4848
4849 let (buffer, range, old_name, new_name) = editor.update(cx, |editor, cx| {
4850 let rename = editor.take_rename(false, cx)?;
4851 let buffer = editor.buffer.read(cx);
4852 let (start_buffer, start) =
4853 buffer.text_anchor_for_position(rename.range.start.clone(), cx)?;
4854 let (end_buffer, end) =
4855 buffer.text_anchor_for_position(rename.range.end.clone(), cx)?;
4856 if start_buffer == end_buffer {
4857 let new_name = rename.editor.read(cx).text(cx);
4858 Some((start_buffer, start..end, rename.old_name, new_name))
4859 } else {
4860 None
4861 }
4862 })?;
4863
4864 let rename = workspace.project().clone().update(cx, |project, cx| {
4865 project.perform_rename(
4866 buffer.clone(),
4867 range.start.clone(),
4868 new_name.clone(),
4869 true,
4870 cx,
4871 )
4872 });
4873
4874 Some(cx.spawn(|workspace, mut cx| async move {
4875 let project_transaction = rename.await?;
4876 Self::open_project_transaction(
4877 editor.clone(),
4878 workspace,
4879 project_transaction,
4880 format!("Rename: {} → {}", old_name, new_name),
4881 cx.clone(),
4882 )
4883 .await?;
4884
4885 editor.update(&mut cx, |editor, cx| {
4886 editor.refresh_document_highlights(cx);
4887 });
4888 Ok(())
4889 }))
4890 }
4891
4892 fn take_rename(
4893 &mut self,
4894 moving_cursor: bool,
4895 cx: &mut ViewContext<Self>,
4896 ) -> Option<RenameState> {
4897 let rename = self.pending_rename.take()?;
4898 self.remove_blocks([rename.block_id].into_iter().collect(), cx);
4899 self.clear_text_highlights::<Rename>(cx);
4900 self.show_local_selections = true;
4901
4902 if moving_cursor {
4903 let rename_editor = rename.editor.read(cx);
4904 let cursor_in_rename_editor = rename_editor.selections.newest::<usize>(cx).head();
4905
4906 // Update the selection to match the position of the selection inside
4907 // the rename editor.
4908 let snapshot = self.buffer.read(cx).read(cx);
4909 let rename_range = rename.range.to_offset(&snapshot);
4910 let cursor_in_editor = snapshot
4911 .clip_offset(rename_range.start + cursor_in_rename_editor, Bias::Left)
4912 .min(rename_range.end);
4913 drop(snapshot);
4914
4915 self.change_selections(None, cx, |s| {
4916 s.select_ranges(vec![cursor_in_editor..cursor_in_editor])
4917 });
4918 }
4919
4920 Some(rename)
4921 }
4922
4923 #[cfg(any(test, feature = "test-support"))]
4924 pub fn pending_rename(&self) -> Option<&RenameState> {
4925 self.pending_rename.as_ref()
4926 }
4927
4928 fn restart_language_server(&mut self, _: &RestartLanguageServer, cx: &mut ViewContext<Self>) {
4929 if let Some(project) = self.project.clone() {
4930 self.buffer.update(cx, |multi_buffer, cx| {
4931 project.update(cx, |project, cx| {
4932 project.restart_language_servers_for_buffers(multi_buffer.all_buffers(), cx);
4933 });
4934 })
4935 }
4936 }
4937
4938 fn refresh_active_diagnostics(&mut self, cx: &mut ViewContext<Editor>) {
4939 if let Some(active_diagnostics) = self.active_diagnostics.as_mut() {
4940 let buffer = self.buffer.read(cx).snapshot(cx);
4941 let primary_range_start = active_diagnostics.primary_range.start.to_offset(&buffer);
4942 let is_valid = buffer
4943 .diagnostics_in_range::<_, usize>(active_diagnostics.primary_range.clone(), false)
4944 .any(|entry| {
4945 entry.diagnostic.is_primary
4946 && !entry.range.is_empty()
4947 && entry.range.start == primary_range_start
4948 && entry.diagnostic.message == active_diagnostics.primary_message
4949 });
4950
4951 if is_valid != active_diagnostics.is_valid {
4952 active_diagnostics.is_valid = is_valid;
4953 let mut new_styles = HashMap::default();
4954 for (block_id, diagnostic) in &active_diagnostics.blocks {
4955 new_styles.insert(
4956 *block_id,
4957 diagnostic_block_renderer(diagnostic.clone(), is_valid),
4958 );
4959 }
4960 self.display_map
4961 .update(cx, |display_map, _| display_map.replace_blocks(new_styles));
4962 }
4963 }
4964 }
4965
4966 fn activate_diagnostics(&mut self, group_id: usize, cx: &mut ViewContext<Self>) {
4967 self.dismiss_diagnostics(cx);
4968 self.active_diagnostics = self.display_map.update(cx, |display_map, cx| {
4969 let buffer = self.buffer.read(cx).snapshot(cx);
4970
4971 let mut primary_range = None;
4972 let mut primary_message = None;
4973 let mut group_end = Point::zero();
4974 let diagnostic_group = buffer
4975 .diagnostic_group::<Point>(group_id)
4976 .map(|entry| {
4977 if entry.range.end > group_end {
4978 group_end = entry.range.end;
4979 }
4980 if entry.diagnostic.is_primary {
4981 primary_range = Some(entry.range.clone());
4982 primary_message = Some(entry.diagnostic.message.clone());
4983 }
4984 entry
4985 })
4986 .collect::<Vec<_>>();
4987 let primary_range = primary_range.unwrap();
4988 let primary_message = primary_message.unwrap();
4989 let primary_range =
4990 buffer.anchor_after(primary_range.start)..buffer.anchor_before(primary_range.end);
4991
4992 let blocks = display_map
4993 .insert_blocks(
4994 diagnostic_group.iter().map(|entry| {
4995 let diagnostic = entry.diagnostic.clone();
4996 let message_height = diagnostic.message.lines().count() as u8;
4997 BlockProperties {
4998 style: BlockStyle::Fixed,
4999 position: buffer.anchor_after(entry.range.start),
5000 height: message_height,
5001 render: diagnostic_block_renderer(diagnostic, true),
5002 disposition: BlockDisposition::Below,
5003 }
5004 }),
5005 cx,
5006 )
5007 .into_iter()
5008 .zip(diagnostic_group.into_iter().map(|entry| entry.diagnostic))
5009 .collect();
5010
5011 Some(ActiveDiagnosticGroup {
5012 primary_range,
5013 primary_message,
5014 blocks,
5015 is_valid: true,
5016 })
5017 });
5018 }
5019
5020 fn dismiss_diagnostics(&mut self, cx: &mut ViewContext<Self>) {
5021 if let Some(active_diagnostic_group) = self.active_diagnostics.take() {
5022 self.display_map.update(cx, |display_map, cx| {
5023 display_map.remove_blocks(active_diagnostic_group.blocks.into_keys().collect(), cx);
5024 });
5025 cx.notify();
5026 }
5027 }
5028
5029 pub fn set_selections_from_remote(
5030 &mut self,
5031 selections: Vec<Selection<Anchor>>,
5032 cx: &mut ViewContext<Self>,
5033 ) {
5034 let old_cursor_position = self.selections.newest_anchor().head();
5035 self.selections.change_with(cx, |s| {
5036 s.select_anchors(selections);
5037 });
5038 self.selections_did_change(false, &old_cursor_position, cx);
5039 }
5040
5041 fn push_to_selection_history(&mut self) {
5042 self.selection_history.push(SelectionHistoryEntry {
5043 selections: self.selections.disjoint_anchors().clone(),
5044 select_next_state: self.select_next_state.clone(),
5045 add_selections_state: self.add_selections_state.clone(),
5046 });
5047 }
5048
5049 pub fn request_autoscroll(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5050 self.autoscroll_request = Some((autoscroll, true));
5051 cx.notify();
5052 }
5053
5054 fn request_autoscroll_remotely(&mut self, autoscroll: Autoscroll, cx: &mut ViewContext<Self>) {
5055 self.autoscroll_request = Some((autoscroll, false));
5056 cx.notify();
5057 }
5058
5059 pub fn transact(
5060 &mut self,
5061 cx: &mut ViewContext<Self>,
5062 update: impl FnOnce(&mut Self, &mut ViewContext<Self>),
5063 ) {
5064 self.start_transaction_at(Instant::now(), cx);
5065 update(self, cx);
5066 self.end_transaction_at(Instant::now(), cx);
5067 }
5068
5069 fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
5070 self.end_selection(cx);
5071 if let Some(tx_id) = self
5072 .buffer
5073 .update(cx, |buffer, cx| buffer.start_transaction_at(now, cx))
5074 {
5075 self.selection_history
5076 .insert_transaction(tx_id, self.selections.disjoint_anchors().clone());
5077 }
5078 }
5079
5080 fn end_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
5081 if let Some(tx_id) = self
5082 .buffer
5083 .update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
5084 {
5085 if let Some((_, end_selections)) = self.selection_history.transaction_mut(tx_id) {
5086 *end_selections = Some(self.selections.disjoint_anchors().clone());
5087 } else {
5088 log::error!("unexpectedly ended a transaction that wasn't started by this editor");
5089 }
5090
5091 cx.emit(Event::Edited);
5092 }
5093 }
5094
5095 pub fn page_up(&mut self, _: &PageUp, _: &mut ViewContext<Self>) {
5096 log::info!("Editor::page_up");
5097 }
5098
5099 pub fn page_down(&mut self, _: &PageDown, _: &mut ViewContext<Self>) {
5100 log::info!("Editor::page_down");
5101 }
5102
5103 pub fn fold(&mut self, _: &Fold, cx: &mut ViewContext<Self>) {
5104 let mut fold_ranges = Vec::new();
5105
5106 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5107 let selections = self.selections.all::<Point>(cx);
5108 for selection in selections {
5109 let range = selection.display_range(&display_map).sorted();
5110 let buffer_start_row = range.start.to_point(&display_map).row;
5111
5112 for row in (0..=range.end.row()).rev() {
5113 if self.is_line_foldable(&display_map, row) && !display_map.is_line_folded(row) {
5114 let fold_range = self.foldable_range_for_line(&display_map, row);
5115 if fold_range.end.row >= buffer_start_row {
5116 fold_ranges.push(fold_range);
5117 if row <= range.start.row() {
5118 break;
5119 }
5120 }
5121 }
5122 }
5123 }
5124
5125 self.fold_ranges(fold_ranges, cx);
5126 }
5127
5128 pub fn unfold_lines(&mut self, _: &UnfoldLines, cx: &mut ViewContext<Self>) {
5129 let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
5130 let buffer = &display_map.buffer_snapshot;
5131 let selections = self.selections.all::<Point>(cx);
5132 let ranges = selections
5133 .iter()
5134 .map(|s| {
5135 let range = s.display_range(&display_map).sorted();
5136 let mut start = range.start.to_point(&display_map);
5137 let mut end = range.end.to_point(&display_map);
5138 start.column = 0;
5139 end.column = buffer.line_len(end.row);
5140 start..end
5141 })
5142 .collect::<Vec<_>>();
5143 self.unfold_ranges(ranges, true, cx);
5144 }
5145
5146 fn is_line_foldable(&self, display_map: &DisplaySnapshot, display_row: u32) -> bool {
5147 let max_point = display_map.max_point();
5148 if display_row >= max_point.row() {
5149 false
5150 } else {
5151 let (start_indent, is_blank) = display_map.line_indent(display_row);
5152 if is_blank {
5153 false
5154 } else {
5155 for display_row in display_row + 1..=max_point.row() {
5156 let (indent, is_blank) = display_map.line_indent(display_row);
5157 if !is_blank {
5158 return indent > start_indent;
5159 }
5160 }
5161 false
5162 }
5163 }
5164 }
5165
5166 fn foldable_range_for_line(
5167 &self,
5168 display_map: &DisplaySnapshot,
5169 start_row: u32,
5170 ) -> Range<Point> {
5171 let max_point = display_map.max_point();
5172
5173 let (start_indent, _) = display_map.line_indent(start_row);
5174 let start = DisplayPoint::new(start_row, display_map.line_len(start_row));
5175 let mut end = None;
5176 for row in start_row + 1..=max_point.row() {
5177 let (indent, is_blank) = display_map.line_indent(row);
5178 if !is_blank && indent <= start_indent {
5179 end = Some(DisplayPoint::new(row - 1, display_map.line_len(row - 1)));
5180 break;
5181 }
5182 }
5183
5184 let end = end.unwrap_or(max_point);
5185 return start.to_point(display_map)..end.to_point(display_map);
5186 }
5187
5188 pub fn fold_selected_ranges(&mut self, _: &FoldSelectedRanges, cx: &mut ViewContext<Self>) {
5189 let selections = self.selections.all::<Point>(cx);
5190 let ranges = selections.into_iter().map(|s| s.start..s.end);
5191 self.fold_ranges(ranges, cx);
5192 }
5193
5194 pub fn fold_ranges<T: ToOffset>(
5195 &mut self,
5196 ranges: impl IntoIterator<Item = Range<T>>,
5197 cx: &mut ViewContext<Self>,
5198 ) {
5199 let mut ranges = ranges.into_iter().peekable();
5200 if ranges.peek().is_some() {
5201 self.display_map.update(cx, |map, cx| map.fold(ranges, cx));
5202 self.request_autoscroll(Autoscroll::Fit, cx);
5203 cx.notify();
5204 }
5205 }
5206
5207 pub fn unfold_ranges<T: ToOffset>(
5208 &mut self,
5209 ranges: impl IntoIterator<Item = Range<T>>,
5210 inclusive: bool,
5211 cx: &mut ViewContext<Self>,
5212 ) {
5213 let mut ranges = ranges.into_iter().peekable();
5214 if ranges.peek().is_some() {
5215 self.display_map
5216 .update(cx, |map, cx| map.unfold(ranges, inclusive, cx));
5217 self.request_autoscroll(Autoscroll::Fit, cx);
5218 cx.notify();
5219 }
5220 }
5221
5222 pub fn insert_blocks(
5223 &mut self,
5224 blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
5225 cx: &mut ViewContext<Self>,
5226 ) -> Vec<BlockId> {
5227 let blocks = self
5228 .display_map
5229 .update(cx, |display_map, cx| display_map.insert_blocks(blocks, cx));
5230 self.request_autoscroll(Autoscroll::Fit, cx);
5231 blocks
5232 }
5233
5234 pub fn replace_blocks(
5235 &mut self,
5236 blocks: HashMap<BlockId, RenderBlock>,
5237 cx: &mut ViewContext<Self>,
5238 ) {
5239 self.display_map
5240 .update(cx, |display_map, _| display_map.replace_blocks(blocks));
5241 self.request_autoscroll(Autoscroll::Fit, cx);
5242 }
5243
5244 pub fn remove_blocks(&mut self, block_ids: HashSet<BlockId>, cx: &mut ViewContext<Self>) {
5245 self.display_map.update(cx, |display_map, cx| {
5246 display_map.remove_blocks(block_ids, cx)
5247 });
5248 }
5249
5250 pub fn longest_row(&self, cx: &mut MutableAppContext) -> u32 {
5251 self.display_map
5252 .update(cx, |map, cx| map.snapshot(cx))
5253 .longest_row()
5254 }
5255
5256 pub fn max_point(&self, cx: &mut MutableAppContext) -> DisplayPoint {
5257 self.display_map
5258 .update(cx, |map, cx| map.snapshot(cx))
5259 .max_point()
5260 }
5261
5262 pub fn text(&self, cx: &AppContext) -> String {
5263 self.buffer.read(cx).read(cx).text()
5264 }
5265
5266 pub fn set_text(&mut self, text: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
5267 self.transact(cx, |this, cx| {
5268 this.buffer
5269 .read(cx)
5270 .as_singleton()
5271 .expect("you can only call set_text on editors for singleton buffers")
5272 .update(cx, |buffer, cx| buffer.set_text(text, cx));
5273 });
5274 }
5275
5276 pub fn display_text(&self, cx: &mut MutableAppContext) -> String {
5277 self.display_map
5278 .update(cx, |map, cx| map.snapshot(cx))
5279 .text()
5280 }
5281
5282 pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
5283 let language_name = self
5284 .buffer
5285 .read(cx)
5286 .as_singleton()
5287 .and_then(|singleton_buffer| singleton_buffer.read(cx).language())
5288 .map(|l| l.name());
5289
5290 let settings = cx.global::<Settings>();
5291 let mode = self
5292 .soft_wrap_mode_override
5293 .unwrap_or_else(|| settings.soft_wrap(language_name.as_deref()));
5294 match mode {
5295 settings::SoftWrap::None => SoftWrap::None,
5296 settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
5297 settings::SoftWrap::PreferredLineLength => {
5298 SoftWrap::Column(settings.preferred_line_length(language_name.as_deref()))
5299 }
5300 }
5301 }
5302
5303 pub fn set_soft_wrap_mode(&mut self, mode: settings::SoftWrap, cx: &mut ViewContext<Self>) {
5304 self.soft_wrap_mode_override = Some(mode);
5305 cx.notify();
5306 }
5307
5308 pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut MutableAppContext) -> bool {
5309 self.display_map
5310 .update(cx, |map, cx| map.set_wrap_width(width, cx))
5311 }
5312
5313 pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
5314 self.highlighted_rows = rows;
5315 }
5316
5317 pub fn highlighted_rows(&self) -> Option<Range<u32>> {
5318 self.highlighted_rows.clone()
5319 }
5320
5321 pub fn highlight_background<T: 'static>(
5322 &mut self,
5323 ranges: Vec<Range<Anchor>>,
5324 color_fetcher: fn(&Theme) -> Color,
5325 cx: &mut ViewContext<Self>,
5326 ) {
5327 self.background_highlights
5328 .insert(TypeId::of::<T>(), (color_fetcher, ranges));
5329 cx.notify();
5330 }
5331
5332 pub fn clear_background_highlights<T: 'static>(
5333 &mut self,
5334 cx: &mut ViewContext<Self>,
5335 ) -> Option<(fn(&Theme) -> Color, Vec<Range<Anchor>>)> {
5336 cx.notify();
5337 self.background_highlights.remove(&TypeId::of::<T>())
5338 }
5339
5340 #[cfg(feature = "test-support")]
5341 pub fn all_background_highlights(
5342 &mut self,
5343 cx: &mut ViewContext<Self>,
5344 ) -> Vec<(Range<DisplayPoint>, Color)> {
5345 let snapshot = self.snapshot(cx);
5346 let buffer = &snapshot.buffer_snapshot;
5347 let start = buffer.anchor_before(0);
5348 let end = buffer.anchor_after(buffer.len());
5349 let theme = cx.global::<Settings>().theme.as_ref();
5350 self.background_highlights_in_range(start..end, &snapshot, theme)
5351 }
5352
5353 fn document_highlights_for_position<'a>(
5354 &'a self,
5355 position: Anchor,
5356 buffer: &'a MultiBufferSnapshot,
5357 ) -> impl 'a + Iterator<Item = &Range<Anchor>> {
5358 let read_highlights = self
5359 .background_highlights
5360 .get(&TypeId::of::<DocumentHighlightRead>())
5361 .map(|h| &h.1);
5362 let write_highlights = self
5363 .background_highlights
5364 .get(&TypeId::of::<DocumentHighlightRead>())
5365 .map(|h| &h.1);
5366 let left_position = position.bias_left(buffer);
5367 let right_position = position.bias_right(buffer);
5368 read_highlights
5369 .into_iter()
5370 .chain(write_highlights)
5371 .flat_map(move |ranges| {
5372 let start_ix = match ranges.binary_search_by(|probe| {
5373 let cmp = probe.end.cmp(&left_position, &buffer);
5374 if cmp.is_ge() {
5375 Ordering::Greater
5376 } else {
5377 Ordering::Less
5378 }
5379 }) {
5380 Ok(i) | Err(i) => i,
5381 };
5382
5383 let right_position = right_position.clone();
5384 ranges[start_ix..]
5385 .iter()
5386 .take_while(move |range| range.start.cmp(&right_position, &buffer).is_le())
5387 })
5388 }
5389
5390 pub fn background_highlights_in_range(
5391 &self,
5392 search_range: Range<Anchor>,
5393 display_snapshot: &DisplaySnapshot,
5394 theme: &Theme,
5395 ) -> Vec<(Range<DisplayPoint>, Color)> {
5396 let mut results = Vec::new();
5397 let buffer = &display_snapshot.buffer_snapshot;
5398 for (color_fetcher, ranges) in self.background_highlights.values() {
5399 let color = color_fetcher(theme);
5400 let start_ix = match ranges.binary_search_by(|probe| {
5401 let cmp = probe.end.cmp(&search_range.start, &buffer);
5402 if cmp.is_gt() {
5403 Ordering::Greater
5404 } else {
5405 Ordering::Less
5406 }
5407 }) {
5408 Ok(i) | Err(i) => i,
5409 };
5410 for range in &ranges[start_ix..] {
5411 if range.start.cmp(&search_range.end, &buffer).is_ge() {
5412 break;
5413 }
5414 let start = range
5415 .start
5416 .to_point(buffer)
5417 .to_display_point(display_snapshot);
5418 let end = range
5419 .end
5420 .to_point(buffer)
5421 .to_display_point(display_snapshot);
5422 results.push((start..end, color))
5423 }
5424 }
5425 results
5426 }
5427
5428 pub fn highlight_text<T: 'static>(
5429 &mut self,
5430 ranges: Vec<Range<Anchor>>,
5431 style: HighlightStyle,
5432 cx: &mut ViewContext<Self>,
5433 ) {
5434 self.display_map.update(cx, |map, _| {
5435 map.highlight_text(TypeId::of::<T>(), ranges, style)
5436 });
5437 cx.notify();
5438 }
5439
5440 pub fn clear_text_highlights<T: 'static>(
5441 &mut self,
5442 cx: &mut ViewContext<Self>,
5443 ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
5444 cx.notify();
5445 self.display_map
5446 .update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()))
5447 }
5448
5449 fn next_blink_epoch(&mut self) -> usize {
5450 self.blink_epoch += 1;
5451 self.blink_epoch
5452 }
5453
5454 fn pause_cursor_blinking(&mut self, cx: &mut ViewContext<Self>) {
5455 if !self.focused {
5456 return;
5457 }
5458
5459 self.show_local_cursors = true;
5460 cx.notify();
5461
5462 let epoch = self.next_blink_epoch();
5463 cx.spawn(|this, mut cx| {
5464 let this = this.downgrade();
5465 async move {
5466 Timer::after(CURSOR_BLINK_INTERVAL).await;
5467 if let Some(this) = this.upgrade(&cx) {
5468 this.update(&mut cx, |this, cx| this.resume_cursor_blinking(epoch, cx))
5469 }
5470 }
5471 })
5472 .detach();
5473 }
5474
5475 fn resume_cursor_blinking(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5476 if epoch == self.blink_epoch {
5477 self.blinking_paused = false;
5478 self.blink_cursors(epoch, cx);
5479 }
5480 }
5481
5482 fn blink_cursors(&mut self, epoch: usize, cx: &mut ViewContext<Self>) {
5483 if epoch == self.blink_epoch && self.focused && !self.blinking_paused {
5484 self.show_local_cursors = !self.show_local_cursors;
5485 cx.notify();
5486
5487 let epoch = self.next_blink_epoch();
5488 cx.spawn(|this, mut cx| {
5489 let this = this.downgrade();
5490 async move {
5491 Timer::after(CURSOR_BLINK_INTERVAL).await;
5492 if let Some(this) = this.upgrade(&cx) {
5493 this.update(&mut cx, |this, cx| this.blink_cursors(epoch, cx));
5494 }
5495 }
5496 })
5497 .detach();
5498 }
5499 }
5500
5501 pub fn show_local_cursors(&self) -> bool {
5502 self.show_local_cursors && self.focused
5503 }
5504
5505 fn on_buffer_changed(&mut self, _: ModelHandle<MultiBuffer>, cx: &mut ViewContext<Self>) {
5506 cx.notify();
5507 }
5508
5509 fn on_buffer_event(
5510 &mut self,
5511 _: ModelHandle<MultiBuffer>,
5512 event: &language::Event,
5513 cx: &mut ViewContext<Self>,
5514 ) {
5515 match event {
5516 language::Event::Edited => {
5517 self.refresh_active_diagnostics(cx);
5518 self.refresh_code_actions(cx);
5519 cx.emit(Event::BufferEdited);
5520 }
5521 language::Event::Reparsed => cx.emit(Event::Reparsed),
5522 language::Event::DirtyChanged => cx.emit(Event::DirtyChanged),
5523 language::Event::Saved => cx.emit(Event::Saved),
5524 language::Event::FileHandleChanged => cx.emit(Event::TitleChanged),
5525 language::Event::Reloaded => cx.emit(Event::TitleChanged),
5526 language::Event::Closed => cx.emit(Event::Closed),
5527 language::Event::DiagnosticsUpdated => {
5528 self.refresh_active_diagnostics(cx);
5529 }
5530 _ => {}
5531 }
5532 }
5533
5534 fn on_display_map_changed(&mut self, _: ModelHandle<DisplayMap>, cx: &mut ViewContext<Self>) {
5535 cx.notify();
5536 }
5537
5538 pub fn set_searchable(&mut self, searchable: bool) {
5539 self.searchable = searchable;
5540 }
5541
5542 pub fn searchable(&self) -> bool {
5543 self.searchable
5544 }
5545
5546 fn open_excerpts(workspace: &mut Workspace, _: &OpenExcerpts, cx: &mut ViewContext<Workspace>) {
5547 let active_item = workspace.active_item(cx);
5548 let editor_handle = if let Some(editor) = active_item
5549 .as_ref()
5550 .and_then(|item| item.act_as::<Self>(cx))
5551 {
5552 editor
5553 } else {
5554 cx.propagate_action();
5555 return;
5556 };
5557
5558 let editor = editor_handle.read(cx);
5559 let buffer = editor.buffer.read(cx);
5560 if buffer.is_singleton() {
5561 cx.propagate_action();
5562 return;
5563 }
5564
5565 let mut new_selections_by_buffer = HashMap::default();
5566 for selection in editor.selections.all::<usize>(cx) {
5567 for (buffer, mut range) in
5568 buffer.range_to_buffer_ranges(selection.start..selection.end, cx)
5569 {
5570 if selection.reversed {
5571 mem::swap(&mut range.start, &mut range.end);
5572 }
5573 new_selections_by_buffer
5574 .entry(buffer)
5575 .or_insert(Vec::new())
5576 .push(range)
5577 }
5578 }
5579
5580 editor_handle.update(cx, |editor, cx| {
5581 editor.push_to_nav_history(editor.selections.newest_anchor().head(), None, cx);
5582 });
5583 let nav_history = workspace.active_pane().read(cx).nav_history().clone();
5584 nav_history.borrow_mut().disable();
5585
5586 // We defer the pane interaction because we ourselves are a workspace item
5587 // and activating a new item causes the pane to call a method on us reentrantly,
5588 // which panics if we're on the stack.
5589 cx.defer(move |workspace, cx| {
5590 for (buffer, ranges) in new_selections_by_buffer.into_iter() {
5591 let editor = workspace.open_project_item::<Self>(buffer, cx);
5592 editor.update(cx, |editor, cx| {
5593 editor.change_selections(Some(Autoscroll::Newest), cx, |s| {
5594 s.select_ranges(ranges);
5595 });
5596 });
5597 }
5598
5599 nav_history.borrow_mut().enable();
5600 });
5601 }
5602
5603 fn jump(workspace: &mut Workspace, action: &Jump, cx: &mut ViewContext<Workspace>) {
5604 let editor = workspace.open_path(action.path.clone(), true, cx);
5605 let position = action.position;
5606 let anchor = action.anchor;
5607 cx.spawn_weak(|_, mut cx| async move {
5608 let editor = editor.await.log_err()?.downcast::<Editor>()?;
5609 editor.update(&mut cx, |editor, cx| {
5610 let buffer = editor.buffer().read(cx).as_singleton()?;
5611 let buffer = buffer.read(cx);
5612 let cursor = if buffer.can_resolve(&anchor) {
5613 language::ToPoint::to_point(&anchor, buffer)
5614 } else {
5615 buffer.clip_point(position, Bias::Left)
5616 };
5617
5618 let nav_history = editor.nav_history.take();
5619 editor.change_selections(Some(Autoscroll::Newest), cx, |s| {
5620 s.select_ranges([cursor..cursor]);
5621 });
5622 editor.nav_history = nav_history;
5623
5624 Some(())
5625 })?;
5626 Some(())
5627 })
5628 .detach()
5629 }
5630}
5631
5632impl EditorSnapshot {
5633 pub fn is_focused(&self) -> bool {
5634 self.is_focused
5635 }
5636
5637 pub fn placeholder_text(&self) -> Option<&Arc<str>> {
5638 self.placeholder_text.as_ref()
5639 }
5640
5641 pub fn scroll_position(&self) -> Vector2F {
5642 compute_scroll_position(
5643 &self.display_snapshot,
5644 self.scroll_position,
5645 &self.scroll_top_anchor,
5646 )
5647 }
5648}
5649
5650impl Deref for EditorSnapshot {
5651 type Target = DisplaySnapshot;
5652
5653 fn deref(&self) -> &Self::Target {
5654 &self.display_snapshot
5655 }
5656}
5657
5658fn compute_scroll_position(
5659 snapshot: &DisplaySnapshot,
5660 mut scroll_position: Vector2F,
5661 scroll_top_anchor: &Anchor,
5662) -> Vector2F {
5663 if *scroll_top_anchor != Anchor::min() {
5664 let scroll_top = scroll_top_anchor.to_display_point(snapshot).row() as f32;
5665 scroll_position.set_y(scroll_top + scroll_position.y());
5666 } else {
5667 scroll_position.set_y(0.);
5668 }
5669 scroll_position
5670}
5671
5672#[derive(Copy, Clone, Debug, PartialEq, Eq)]
5673pub enum Event {
5674 Activate,
5675 BufferEdited,
5676 Edited,
5677 Reparsed,
5678 Blurred,
5679 DirtyChanged,
5680 Saved,
5681 TitleChanged,
5682 SelectionsChanged { local: bool },
5683 ScrollPositionChanged { local: bool },
5684 Closed,
5685}
5686
5687pub struct EditorFocused(pub ViewHandle<Editor>);
5688pub struct EditorBlurred(pub ViewHandle<Editor>);
5689pub struct EditorReleased(pub WeakViewHandle<Editor>);
5690
5691impl Entity for Editor {
5692 type Event = Event;
5693
5694 fn release(&mut self, cx: &mut MutableAppContext) {
5695 cx.emit_global(EditorReleased(self.handle.clone()));
5696 }
5697}
5698
5699impl View for Editor {
5700 fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
5701 let style = self.style(cx);
5702 let font_changed = self.display_map.update(cx, |map, cx| {
5703 map.set_font(style.text.font_id, style.text.font_size, cx)
5704 });
5705
5706 if font_changed {
5707 let handle = self.handle.clone();
5708 cx.defer(move |cx| {
5709 if let Some(editor) = handle.upgrade(cx) {
5710 editor.update(cx, |editor, cx| {
5711 hide_hover(editor, cx);
5712 })
5713 }
5714 });
5715 }
5716
5717 EditorElement::new(self.handle.clone(), style.clone(), self.cursor_shape).boxed()
5718 }
5719
5720 fn ui_name() -> &'static str {
5721 "Editor"
5722 }
5723
5724 fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
5725 let focused_event = EditorFocused(cx.handle());
5726 cx.emit_global(focused_event);
5727 if let Some(rename) = self.pending_rename.as_ref() {
5728 cx.focus(&rename.editor);
5729 } else {
5730 self.focused = true;
5731 self.blink_cursors(self.blink_epoch, cx);
5732 self.buffer.update(cx, |buffer, cx| {
5733 buffer.finalize_last_transaction(cx);
5734 if self.leader_replica_id.is_none() {
5735 buffer.set_active_selections(
5736 &self.selections.disjoint_anchors(),
5737 self.selections.line_mode,
5738 cx,
5739 );
5740 }
5741 });
5742 }
5743 }
5744
5745 fn on_blur(&mut self, cx: &mut ViewContext<Self>) {
5746 let blurred_event = EditorBlurred(cx.handle());
5747 cx.emit_global(blurred_event);
5748 self.focused = false;
5749 self.buffer
5750 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
5751 self.hide_context_menu(cx);
5752 hide_hover(self, cx);
5753 cx.emit(Event::Blurred);
5754 cx.notify();
5755 }
5756
5757 fn keymap_context(&self, _: &AppContext) -> gpui::keymap::Context {
5758 let mut context = Self::default_keymap_context();
5759 let mode = match self.mode {
5760 EditorMode::SingleLine => "single_line",
5761 EditorMode::AutoHeight { .. } => "auto_height",
5762 EditorMode::Full => "full",
5763 };
5764 context.map.insert("mode".into(), mode.into());
5765 if self.pending_rename.is_some() {
5766 context.set.insert("renaming".into());
5767 }
5768 match self.context_menu.as_ref() {
5769 Some(ContextMenu::Completions(_)) => {
5770 context.set.insert("showing_completions".into());
5771 }
5772 Some(ContextMenu::CodeActions(_)) => {
5773 context.set.insert("showing_code_actions".into());
5774 }
5775 None => {}
5776 }
5777
5778 for layer in self.keymap_context_layers.values() {
5779 context.extend(layer);
5780 }
5781
5782 context
5783 }
5784}
5785
5786fn build_style(
5787 settings: &Settings,
5788 get_field_editor_theme: Option<GetFieldEditorTheme>,
5789 override_text_style: Option<&OverrideTextStyle>,
5790 cx: &AppContext,
5791) -> EditorStyle {
5792 let font_cache = cx.font_cache();
5793
5794 let mut theme = settings.theme.editor.clone();
5795 let mut style = if let Some(get_field_editor_theme) = get_field_editor_theme {
5796 let field_editor_theme = get_field_editor_theme(&settings.theme);
5797 theme.text_color = field_editor_theme.text.color;
5798 theme.selection = field_editor_theme.selection;
5799 theme.background = field_editor_theme
5800 .container
5801 .background_color
5802 .unwrap_or_default();
5803 EditorStyle {
5804 text: field_editor_theme.text,
5805 placeholder_text: field_editor_theme.placeholder_text,
5806 theme,
5807 }
5808 } else {
5809 let font_family_id = settings.buffer_font_family;
5810 let font_family_name = cx.font_cache().family_name(font_family_id).unwrap();
5811 let font_properties = Default::default();
5812 let font_id = font_cache
5813 .select_font(font_family_id, &font_properties)
5814 .unwrap();
5815 let font_size = settings.buffer_font_size;
5816 EditorStyle {
5817 text: TextStyle {
5818 color: settings.theme.editor.text_color,
5819 font_family_name,
5820 font_family_id,
5821 font_id,
5822 font_size,
5823 font_properties,
5824 underline: Default::default(),
5825 },
5826 placeholder_text: None,
5827 theme,
5828 }
5829 };
5830
5831 if let Some(highlight_style) = override_text_style.and_then(|build_style| build_style(&style)) {
5832 if let Some(highlighted) = style
5833 .text
5834 .clone()
5835 .highlight(highlight_style, font_cache)
5836 .log_err()
5837 {
5838 style.text = highlighted;
5839 }
5840 }
5841
5842 style
5843}
5844
5845trait SelectionExt {
5846 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize>;
5847 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point>;
5848 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint>;
5849 fn spanned_rows(&self, include_end_if_at_line_start: bool, map: &DisplaySnapshot)
5850 -> Range<u32>;
5851}
5852
5853impl<T: ToPoint + ToOffset> SelectionExt for Selection<T> {
5854 fn point_range(&self, buffer: &MultiBufferSnapshot) -> Range<Point> {
5855 let start = self.start.to_point(buffer);
5856 let end = self.end.to_point(buffer);
5857 if self.reversed {
5858 end..start
5859 } else {
5860 start..end
5861 }
5862 }
5863
5864 fn offset_range(&self, buffer: &MultiBufferSnapshot) -> Range<usize> {
5865 let start = self.start.to_offset(buffer);
5866 let end = self.end.to_offset(buffer);
5867 if self.reversed {
5868 end..start
5869 } else {
5870 start..end
5871 }
5872 }
5873
5874 fn display_range(&self, map: &DisplaySnapshot) -> Range<DisplayPoint> {
5875 let start = self
5876 .start
5877 .to_point(&map.buffer_snapshot)
5878 .to_display_point(map);
5879 let end = self
5880 .end
5881 .to_point(&map.buffer_snapshot)
5882 .to_display_point(map);
5883 if self.reversed {
5884 end..start
5885 } else {
5886 start..end
5887 }
5888 }
5889
5890 fn spanned_rows(
5891 &self,
5892 include_end_if_at_line_start: bool,
5893 map: &DisplaySnapshot,
5894 ) -> Range<u32> {
5895 let start = self.start.to_point(&map.buffer_snapshot);
5896 let mut end = self.end.to_point(&map.buffer_snapshot);
5897 if !include_end_if_at_line_start && start.row != end.row && end.column == 0 {
5898 end.row -= 1;
5899 }
5900
5901 let buffer_start = map.prev_line_boundary(start).0;
5902 let buffer_end = map.next_line_boundary(end).0;
5903 buffer_start.row..buffer_end.row + 1
5904 }
5905}
5906
5907impl<T: InvalidationRegion> InvalidationStack<T> {
5908 fn invalidate<S>(&mut self, selections: &[Selection<S>], buffer: &MultiBufferSnapshot)
5909 where
5910 S: Clone + ToOffset,
5911 {
5912 while let Some(region) = self.last() {
5913 let all_selections_inside_invalidation_ranges =
5914 if selections.len() == region.ranges().len() {
5915 selections
5916 .iter()
5917 .zip(region.ranges().iter().map(|r| r.to_offset(&buffer)))
5918 .all(|(selection, invalidation_range)| {
5919 let head = selection.head().to_offset(&buffer);
5920 invalidation_range.start <= head && invalidation_range.end >= head
5921 })
5922 } else {
5923 false
5924 };
5925
5926 if all_selections_inside_invalidation_ranges {
5927 break;
5928 } else {
5929 self.pop();
5930 }
5931 }
5932 }
5933}
5934
5935impl<T> Default for InvalidationStack<T> {
5936 fn default() -> Self {
5937 Self(Default::default())
5938 }
5939}
5940
5941impl<T> Deref for InvalidationStack<T> {
5942 type Target = Vec<T>;
5943
5944 fn deref(&self) -> &Self::Target {
5945 &self.0
5946 }
5947}
5948
5949impl<T> DerefMut for InvalidationStack<T> {
5950 fn deref_mut(&mut self) -> &mut Self::Target {
5951 &mut self.0
5952 }
5953}
5954
5955impl InvalidationRegion for BracketPairState {
5956 fn ranges(&self) -> &[Range<Anchor>] {
5957 &self.ranges
5958 }
5959}
5960
5961impl InvalidationRegion for SnippetState {
5962 fn ranges(&self) -> &[Range<Anchor>] {
5963 &self.ranges[self.active_index]
5964 }
5965}
5966
5967impl Deref for EditorStyle {
5968 type Target = theme::Editor;
5969
5970 fn deref(&self) -> &Self::Target {
5971 &self.theme
5972 }
5973}
5974
5975pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> RenderBlock {
5976 let mut highlighted_lines = Vec::new();
5977 for line in diagnostic.message.lines() {
5978 highlighted_lines.push(highlight_diagnostic_message(line));
5979 }
5980
5981 Arc::new(move |cx: &mut BlockContext| {
5982 let settings = cx.global::<Settings>();
5983 let theme = &settings.theme.editor;
5984 let style = diagnostic_style(diagnostic.severity, is_valid, theme);
5985 let font_size = (style.text_scale_factor * settings.buffer_font_size).round();
5986 Flex::column()
5987 .with_children(highlighted_lines.iter().map(|(line, highlights)| {
5988 Label::new(
5989 line.clone(),
5990 style.message.clone().with_font_size(font_size),
5991 )
5992 .with_highlights(highlights.clone())
5993 .contained()
5994 .with_margin_left(cx.anchor_x)
5995 .boxed()
5996 }))
5997 .aligned()
5998 .left()
5999 .boxed()
6000 })
6001}
6002
6003pub fn highlight_diagnostic_message(message: &str) -> (String, Vec<usize>) {
6004 let mut message_without_backticks = String::new();
6005 let mut prev_offset = 0;
6006 let mut inside_block = false;
6007 let mut highlights = Vec::new();
6008 for (match_ix, (offset, _)) in message
6009 .match_indices('`')
6010 .chain([(message.len(), "")])
6011 .enumerate()
6012 {
6013 message_without_backticks.push_str(&message[prev_offset..offset]);
6014 if inside_block {
6015 highlights.extend(prev_offset - match_ix..offset - match_ix);
6016 }
6017
6018 inside_block = !inside_block;
6019 prev_offset = offset + 1;
6020 }
6021
6022 (message_without_backticks, highlights)
6023}
6024
6025pub fn diagnostic_style(
6026 severity: DiagnosticSeverity,
6027 valid: bool,
6028 theme: &theme::Editor,
6029) -> DiagnosticStyle {
6030 match (severity, valid) {
6031 (DiagnosticSeverity::ERROR, true) => theme.error_diagnostic.clone(),
6032 (DiagnosticSeverity::ERROR, false) => theme.invalid_error_diagnostic.clone(),
6033 (DiagnosticSeverity::WARNING, true) => theme.warning_diagnostic.clone(),
6034 (DiagnosticSeverity::WARNING, false) => theme.invalid_warning_diagnostic.clone(),
6035 (DiagnosticSeverity::INFORMATION, true) => theme.information_diagnostic.clone(),
6036 (DiagnosticSeverity::INFORMATION, false) => theme.invalid_information_diagnostic.clone(),
6037 (DiagnosticSeverity::HINT, true) => theme.hint_diagnostic.clone(),
6038 (DiagnosticSeverity::HINT, false) => theme.invalid_hint_diagnostic.clone(),
6039 _ => theme.invalid_hint_diagnostic.clone(),
6040 }
6041}
6042
6043pub fn combine_syntax_and_fuzzy_match_highlights(
6044 text: &str,
6045 default_style: HighlightStyle,
6046 syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
6047 match_indices: &[usize],
6048) -> Vec<(Range<usize>, HighlightStyle)> {
6049 let mut result = Vec::new();
6050 let mut match_indices = match_indices.iter().copied().peekable();
6051
6052 for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
6053 {
6054 syntax_highlight.weight = None;
6055
6056 // Add highlights for any fuzzy match characters before the next
6057 // syntax highlight range.
6058 while let Some(&match_index) = match_indices.peek() {
6059 if match_index >= range.start {
6060 break;
6061 }
6062 match_indices.next();
6063 let end_index = char_ix_after(match_index, text);
6064 let mut match_style = default_style;
6065 match_style.weight = Some(fonts::Weight::BOLD);
6066 result.push((match_index..end_index, match_style));
6067 }
6068
6069 if range.start == usize::MAX {
6070 break;
6071 }
6072
6073 // Add highlights for any fuzzy match characters within the
6074 // syntax highlight range.
6075 let mut offset = range.start;
6076 while let Some(&match_index) = match_indices.peek() {
6077 if match_index >= range.end {
6078 break;
6079 }
6080
6081 match_indices.next();
6082 if match_index > offset {
6083 result.push((offset..match_index, syntax_highlight));
6084 }
6085
6086 let mut end_index = char_ix_after(match_index, text);
6087 while let Some(&next_match_index) = match_indices.peek() {
6088 if next_match_index == end_index && next_match_index < range.end {
6089 end_index = char_ix_after(next_match_index, text);
6090 match_indices.next();
6091 } else {
6092 break;
6093 }
6094 }
6095
6096 let mut match_style = syntax_highlight;
6097 match_style.weight = Some(fonts::Weight::BOLD);
6098 result.push((match_index..end_index, match_style));
6099 offset = end_index;
6100 }
6101
6102 if offset < range.end {
6103 result.push((offset..range.end, syntax_highlight));
6104 }
6105 }
6106
6107 fn char_ix_after(ix: usize, text: &str) -> usize {
6108 ix + text[ix..].chars().next().unwrap().len_utf8()
6109 }
6110
6111 result
6112}
6113
6114pub fn styled_runs_for_code_label<'a>(
6115 label: &'a CodeLabel,
6116 syntax_theme: &'a theme::SyntaxTheme,
6117) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
6118 let fade_out = HighlightStyle {
6119 fade_out: Some(0.35),
6120 ..Default::default()
6121 };
6122
6123 let mut prev_end = label.filter_range.end;
6124 label
6125 .runs
6126 .iter()
6127 .enumerate()
6128 .flat_map(move |(ix, (range, highlight_id))| {
6129 let style = if let Some(style) = highlight_id.style(syntax_theme) {
6130 style
6131 } else {
6132 return Default::default();
6133 };
6134 let mut muted_style = style.clone();
6135 muted_style.highlight(fade_out);
6136
6137 let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
6138 if range.start >= label.filter_range.end {
6139 if range.start > prev_end {
6140 runs.push((prev_end..range.start, fade_out));
6141 }
6142 runs.push((range.clone(), muted_style));
6143 } else if range.end <= label.filter_range.end {
6144 runs.push((range.clone(), style));
6145 } else {
6146 runs.push((range.start..label.filter_range.end, style));
6147 runs.push((label.filter_range.end..range.end, muted_style));
6148 }
6149 prev_end = cmp::max(prev_end, range.end);
6150
6151 if ix + 1 == label.runs.len() && label.text.len() > prev_end {
6152 runs.push((prev_end..label.text.len(), fade_out));
6153 }
6154
6155 runs
6156 })
6157}
6158
6159#[cfg(test)]
6160mod tests {
6161 use crate::test::{
6162 assert_text_with_selections, build_editor, select_ranges, EditorTestContext,
6163 };
6164
6165 use super::*;
6166 use futures::StreamExt;
6167 use gpui::{
6168 geometry::rect::RectF,
6169 platform::{WindowBounds, WindowOptions},
6170 };
6171 use indoc::indoc;
6172 use language::{FakeLspAdapter, LanguageConfig};
6173 use lsp::FakeLanguageServer;
6174 use project::FakeFs;
6175 use settings::LanguageSettings;
6176 use std::{cell::RefCell, rc::Rc, time::Instant};
6177 use text::Point;
6178 use unindent::Unindent;
6179 use util::{
6180 assert_set_eq,
6181 test::{marked_text_by, marked_text_ranges, marked_text_ranges_by, sample_text},
6182 };
6183 use workspace::{FollowableItem, ItemHandle};
6184
6185 #[gpui::test]
6186 fn test_edit_events(cx: &mut MutableAppContext) {
6187 cx.set_global(Settings::test(cx));
6188 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6189
6190 let events = Rc::new(RefCell::new(Vec::new()));
6191 let (_, editor1) = cx.add_window(Default::default(), {
6192 let events = events.clone();
6193 |cx| {
6194 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6195 if matches!(
6196 event,
6197 Event::Edited | Event::BufferEdited | Event::DirtyChanged
6198 ) {
6199 events.borrow_mut().push(("editor1", *event));
6200 }
6201 })
6202 .detach();
6203 Editor::for_buffer(buffer.clone(), None, cx)
6204 }
6205 });
6206 let (_, editor2) = cx.add_window(Default::default(), {
6207 let events = events.clone();
6208 |cx| {
6209 cx.subscribe(&cx.handle(), move |_, _, event, _| {
6210 if matches!(
6211 event,
6212 Event::Edited | Event::BufferEdited | Event::DirtyChanged
6213 ) {
6214 events.borrow_mut().push(("editor2", *event));
6215 }
6216 })
6217 .detach();
6218 Editor::for_buffer(buffer.clone(), None, cx)
6219 }
6220 });
6221 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6222
6223 // Mutating editor 1 will emit an `Edited` event only for that editor.
6224 editor1.update(cx, |editor, cx| editor.insert("X", cx));
6225 assert_eq!(
6226 mem::take(&mut *events.borrow_mut()),
6227 [
6228 ("editor1", Event::Edited),
6229 ("editor1", Event::BufferEdited),
6230 ("editor2", Event::BufferEdited),
6231 ("editor1", Event::DirtyChanged),
6232 ("editor2", Event::DirtyChanged)
6233 ]
6234 );
6235
6236 // Mutating editor 2 will emit an `Edited` event only for that editor.
6237 editor2.update(cx, |editor, cx| editor.delete(&Delete, cx));
6238 assert_eq!(
6239 mem::take(&mut *events.borrow_mut()),
6240 [
6241 ("editor2", Event::Edited),
6242 ("editor1", Event::BufferEdited),
6243 ("editor2", Event::BufferEdited),
6244 ]
6245 );
6246
6247 // Undoing on editor 1 will emit an `Edited` event only for that editor.
6248 editor1.update(cx, |editor, cx| editor.undo(&Undo, cx));
6249 assert_eq!(
6250 mem::take(&mut *events.borrow_mut()),
6251 [
6252 ("editor1", Event::Edited),
6253 ("editor1", Event::BufferEdited),
6254 ("editor2", Event::BufferEdited),
6255 ("editor1", Event::DirtyChanged),
6256 ("editor2", Event::DirtyChanged),
6257 ]
6258 );
6259
6260 // Redoing on editor 1 will emit an `Edited` event only for that editor.
6261 editor1.update(cx, |editor, cx| editor.redo(&Redo, cx));
6262 assert_eq!(
6263 mem::take(&mut *events.borrow_mut()),
6264 [
6265 ("editor1", Event::Edited),
6266 ("editor1", Event::BufferEdited),
6267 ("editor2", Event::BufferEdited),
6268 ("editor1", Event::DirtyChanged),
6269 ("editor2", Event::DirtyChanged),
6270 ]
6271 );
6272
6273 // Undoing on editor 2 will emit an `Edited` event only for that editor.
6274 editor2.update(cx, |editor, cx| editor.undo(&Undo, cx));
6275 assert_eq!(
6276 mem::take(&mut *events.borrow_mut()),
6277 [
6278 ("editor2", Event::Edited),
6279 ("editor1", Event::BufferEdited),
6280 ("editor2", Event::BufferEdited),
6281 ("editor1", Event::DirtyChanged),
6282 ("editor2", Event::DirtyChanged),
6283 ]
6284 );
6285
6286 // Redoing on editor 2 will emit an `Edited` event only for that editor.
6287 editor2.update(cx, |editor, cx| editor.redo(&Redo, cx));
6288 assert_eq!(
6289 mem::take(&mut *events.borrow_mut()),
6290 [
6291 ("editor2", Event::Edited),
6292 ("editor1", Event::BufferEdited),
6293 ("editor2", Event::BufferEdited),
6294 ("editor1", Event::DirtyChanged),
6295 ("editor2", Event::DirtyChanged),
6296 ]
6297 );
6298
6299 // No event is emitted when the mutation is a no-op.
6300 editor2.update(cx, |editor, cx| {
6301 editor.change_selections(None, cx, |s| s.select_ranges([0..0]));
6302
6303 editor.backspace(&Backspace, cx);
6304 });
6305 assert_eq!(mem::take(&mut *events.borrow_mut()), []);
6306 }
6307
6308 #[gpui::test]
6309 fn test_undo_redo_with_selection_restoration(cx: &mut MutableAppContext) {
6310 cx.set_global(Settings::test(cx));
6311 let mut now = Instant::now();
6312 let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
6313 let group_interval = buffer.read(cx).transaction_group_interval();
6314 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
6315 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6316
6317 editor.update(cx, |editor, cx| {
6318 editor.start_transaction_at(now, cx);
6319 editor.change_selections(None, cx, |s| s.select_ranges([2..4]));
6320
6321 editor.insert("cd", cx);
6322 editor.end_transaction_at(now, cx);
6323 assert_eq!(editor.text(cx), "12cd56");
6324 assert_eq!(editor.selections.ranges(cx), vec![4..4]);
6325
6326 editor.start_transaction_at(now, cx);
6327 editor.change_selections(None, cx, |s| s.select_ranges([4..5]));
6328 editor.insert("e", cx);
6329 editor.end_transaction_at(now, cx);
6330 assert_eq!(editor.text(cx), "12cde6");
6331 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6332
6333 now += group_interval + Duration::from_millis(1);
6334 editor.change_selections(None, cx, |s| s.select_ranges([2..2]));
6335
6336 // Simulate an edit in another editor
6337 buffer.update(cx, |buffer, cx| {
6338 buffer.start_transaction_at(now, cx);
6339 buffer.edit([(0..1, "a")], cx);
6340 buffer.edit([(1..1, "b")], cx);
6341 buffer.end_transaction_at(now, cx);
6342 });
6343
6344 assert_eq!(editor.text(cx), "ab2cde6");
6345 assert_eq!(editor.selections.ranges(cx), vec![3..3]);
6346
6347 // Last transaction happened past the group interval in a different editor.
6348 // Undo it individually and don't restore selections.
6349 editor.undo(&Undo, cx);
6350 assert_eq!(editor.text(cx), "12cde6");
6351 assert_eq!(editor.selections.ranges(cx), vec![2..2]);
6352
6353 // First two transactions happened within the group interval in this editor.
6354 // Undo them together and restore selections.
6355 editor.undo(&Undo, cx);
6356 editor.undo(&Undo, cx); // Undo stack is empty here, so this is a no-op.
6357 assert_eq!(editor.text(cx), "123456");
6358 assert_eq!(editor.selections.ranges(cx), vec![0..0]);
6359
6360 // Redo the first two transactions together.
6361 editor.redo(&Redo, cx);
6362 assert_eq!(editor.text(cx), "12cde6");
6363 assert_eq!(editor.selections.ranges(cx), vec![5..5]);
6364
6365 // Redo the last transaction on its own.
6366 editor.redo(&Redo, cx);
6367 assert_eq!(editor.text(cx), "ab2cde6");
6368 assert_eq!(editor.selections.ranges(cx), vec![6..6]);
6369
6370 // Test empty transactions.
6371 editor.start_transaction_at(now, cx);
6372 editor.end_transaction_at(now, cx);
6373 editor.undo(&Undo, cx);
6374 assert_eq!(editor.text(cx), "12cde6");
6375 });
6376 }
6377
6378 #[gpui::test]
6379 fn test_selection_with_mouse(cx: &mut gpui::MutableAppContext) {
6380 cx.set_global(Settings::test(cx));
6381
6382 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
6383 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6384 editor.update(cx, |view, cx| {
6385 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6386 });
6387 assert_eq!(
6388 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6389 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6390 );
6391
6392 editor.update(cx, |view, cx| {
6393 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6394 });
6395
6396 assert_eq!(
6397 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6398 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6399 );
6400
6401 editor.update(cx, |view, cx| {
6402 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6403 });
6404
6405 assert_eq!(
6406 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6407 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6408 );
6409
6410 editor.update(cx, |view, cx| {
6411 view.end_selection(cx);
6412 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6413 });
6414
6415 assert_eq!(
6416 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6417 [DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1)]
6418 );
6419
6420 editor.update(cx, |view, cx| {
6421 view.begin_selection(DisplayPoint::new(3, 3), true, 1, cx);
6422 view.update_selection(DisplayPoint::new(0, 0), 0, Vector2F::zero(), cx);
6423 });
6424
6425 assert_eq!(
6426 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6427 [
6428 DisplayPoint::new(2, 2)..DisplayPoint::new(1, 1),
6429 DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)
6430 ]
6431 );
6432
6433 editor.update(cx, |view, cx| {
6434 view.end_selection(cx);
6435 });
6436
6437 assert_eq!(
6438 editor.update(cx, |view, cx| view.selections.display_ranges(cx)),
6439 [DisplayPoint::new(3, 3)..DisplayPoint::new(0, 0)]
6440 );
6441 }
6442
6443 #[gpui::test]
6444 fn test_canceling_pending_selection(cx: &mut gpui::MutableAppContext) {
6445 cx.set_global(Settings::test(cx));
6446 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6447 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6448
6449 view.update(cx, |view, cx| {
6450 view.begin_selection(DisplayPoint::new(2, 2), false, 1, cx);
6451 assert_eq!(
6452 view.selections.display_ranges(cx),
6453 [DisplayPoint::new(2, 2)..DisplayPoint::new(2, 2)]
6454 );
6455 });
6456
6457 view.update(cx, |view, cx| {
6458 view.update_selection(DisplayPoint::new(3, 3), 0, Vector2F::zero(), cx);
6459 assert_eq!(
6460 view.selections.display_ranges(cx),
6461 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6462 );
6463 });
6464
6465 view.update(cx, |view, cx| {
6466 view.cancel(&Cancel, cx);
6467 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6468 assert_eq!(
6469 view.selections.display_ranges(cx),
6470 [DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3)]
6471 );
6472 });
6473 }
6474
6475 #[gpui::test]
6476 fn test_clone(cx: &mut gpui::MutableAppContext) {
6477 let (text, selection_ranges) = marked_text_ranges(indoc! {"
6478 one
6479 two
6480 three[]
6481 four
6482 five[]
6483 "});
6484 cx.set_global(Settings::test(cx));
6485 let buffer = MultiBuffer::build_simple(&text, cx);
6486
6487 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6488
6489 editor.update(cx, |editor, cx| {
6490 editor.change_selections(None, cx, |s| s.select_ranges(selection_ranges.clone()));
6491 editor.fold_ranges(
6492 [
6493 Point::new(1, 0)..Point::new(2, 0),
6494 Point::new(3, 0)..Point::new(4, 0),
6495 ],
6496 cx,
6497 );
6498 });
6499
6500 let (_, cloned_editor) = editor.update(cx, |editor, cx| {
6501 cx.add_window(Default::default(), |cx| editor.clone(cx))
6502 });
6503
6504 let snapshot = editor.update(cx, |e, cx| e.snapshot(cx));
6505 let cloned_snapshot = cloned_editor.update(cx, |e, cx| e.snapshot(cx));
6506
6507 assert_eq!(
6508 cloned_editor.update(cx, |e, cx| e.display_text(cx)),
6509 editor.update(cx, |e, cx| e.display_text(cx))
6510 );
6511 assert_eq!(
6512 cloned_snapshot
6513 .folds_in_range(0..text.len())
6514 .collect::<Vec<_>>(),
6515 snapshot.folds_in_range(0..text.len()).collect::<Vec<_>>(),
6516 );
6517 assert_set_eq!(
6518 cloned_editor.read(cx).selections.ranges::<Point>(cx),
6519 editor.read(cx).selections.ranges(cx)
6520 );
6521 assert_set_eq!(
6522 cloned_editor.update(cx, |e, cx| dbg!(e.selections.display_ranges(cx))),
6523 editor.update(cx, |e, cx| dbg!(e.selections.display_ranges(cx)))
6524 );
6525 }
6526
6527 #[gpui::test]
6528 fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
6529 cx.set_global(Settings::test(cx));
6530 use workspace::Item;
6531 let nav_history = Rc::new(RefCell::new(workspace::NavHistory::default()));
6532 let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
6533
6534 cx.add_window(Default::default(), |cx| {
6535 let mut editor = build_editor(buffer.clone(), cx);
6536 editor.nav_history = Some(ItemNavHistory::new(nav_history.clone(), &cx.handle()));
6537
6538 // Move the cursor a small distance.
6539 // Nothing is added to the navigation history.
6540 editor.change_selections(None, cx, |s| {
6541 s.select_display_ranges([DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)])
6542 });
6543 editor.change_selections(None, cx, |s| {
6544 s.select_display_ranges([DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)])
6545 });
6546 assert!(nav_history.borrow_mut().pop_backward().is_none());
6547
6548 // Move the cursor a large distance.
6549 // The history can jump back to the previous position.
6550 editor.change_selections(None, cx, |s| {
6551 s.select_display_ranges([DisplayPoint::new(13, 0)..DisplayPoint::new(13, 3)])
6552 });
6553 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6554 editor.navigate(nav_entry.data.unwrap(), cx);
6555 assert_eq!(nav_entry.item.id(), cx.view_id());
6556 assert_eq!(
6557 editor.selections.display_ranges(cx),
6558 &[DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0)]
6559 );
6560 assert!(nav_history.borrow_mut().pop_backward().is_none());
6561
6562 // Move the cursor a small distance via the mouse.
6563 // Nothing is added to the navigation history.
6564 editor.begin_selection(DisplayPoint::new(5, 0), false, 1, cx);
6565 editor.end_selection(cx);
6566 assert_eq!(
6567 editor.selections.display_ranges(cx),
6568 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6569 );
6570 assert!(nav_history.borrow_mut().pop_backward().is_none());
6571
6572 // Move the cursor a large distance via the mouse.
6573 // The history can jump back to the previous position.
6574 editor.begin_selection(DisplayPoint::new(15, 0), false, 1, cx);
6575 editor.end_selection(cx);
6576 assert_eq!(
6577 editor.selections.display_ranges(cx),
6578 &[DisplayPoint::new(15, 0)..DisplayPoint::new(15, 0)]
6579 );
6580 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6581 editor.navigate(nav_entry.data.unwrap(), cx);
6582 assert_eq!(nav_entry.item.id(), cx.view_id());
6583 assert_eq!(
6584 editor.selections.display_ranges(cx),
6585 &[DisplayPoint::new(5, 0)..DisplayPoint::new(5, 0)]
6586 );
6587 assert!(nav_history.borrow_mut().pop_backward().is_none());
6588
6589 // Set scroll position to check later
6590 editor.set_scroll_position(Vector2F::new(5.5, 5.5), cx);
6591 let original_scroll_position = editor.scroll_position;
6592 let original_scroll_top_anchor = editor.scroll_top_anchor.clone();
6593
6594 // Jump to the end of the document and adjust scroll
6595 editor.move_to_end(&MoveToEnd, cx);
6596 editor.set_scroll_position(Vector2F::new(-2.5, -0.5), cx);
6597 assert_ne!(editor.scroll_position, original_scroll_position);
6598 assert_ne!(editor.scroll_top_anchor, original_scroll_top_anchor);
6599
6600 let nav_entry = nav_history.borrow_mut().pop_backward().unwrap();
6601 editor.navigate(nav_entry.data.unwrap(), cx);
6602 assert_eq!(editor.scroll_position, original_scroll_position);
6603 assert_eq!(editor.scroll_top_anchor, original_scroll_top_anchor);
6604
6605 // Ensure we don't panic when navigation data contains invalid anchors *and* points.
6606 let mut invalid_anchor = editor.scroll_top_anchor.clone();
6607 invalid_anchor.text_anchor.buffer_id = Some(999);
6608 let invalid_point = Point::new(9999, 0);
6609 editor.navigate(
6610 Box::new(NavigationData {
6611 cursor_anchor: invalid_anchor.clone(),
6612 cursor_position: invalid_point,
6613 scroll_top_anchor: invalid_anchor.clone(),
6614 scroll_top_row: invalid_point.row,
6615 scroll_position: Default::default(),
6616 }),
6617 cx,
6618 );
6619 assert_eq!(
6620 editor.selections.display_ranges(cx),
6621 &[editor.max_point(cx)..editor.max_point(cx)]
6622 );
6623 assert_eq!(
6624 editor.scroll_position(cx),
6625 vec2f(0., editor.max_point(cx).row() as f32)
6626 );
6627
6628 editor
6629 });
6630 }
6631
6632 #[gpui::test]
6633 fn test_cancel(cx: &mut gpui::MutableAppContext) {
6634 cx.set_global(Settings::test(cx));
6635 let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
6636 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6637
6638 view.update(cx, |view, cx| {
6639 view.begin_selection(DisplayPoint::new(3, 4), false, 1, cx);
6640 view.update_selection(DisplayPoint::new(1, 1), 0, Vector2F::zero(), cx);
6641 view.end_selection(cx);
6642
6643 view.begin_selection(DisplayPoint::new(0, 1), true, 1, cx);
6644 view.update_selection(DisplayPoint::new(0, 3), 0, Vector2F::zero(), cx);
6645 view.end_selection(cx);
6646 assert_eq!(
6647 view.selections.display_ranges(cx),
6648 [
6649 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
6650 DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1),
6651 ]
6652 );
6653 });
6654
6655 view.update(cx, |view, cx| {
6656 view.cancel(&Cancel, cx);
6657 assert_eq!(
6658 view.selections.display_ranges(cx),
6659 [DisplayPoint::new(3, 4)..DisplayPoint::new(1, 1)]
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(1, 1)..DisplayPoint::new(1, 1)]
6668 );
6669 });
6670 }
6671
6672 #[gpui::test]
6673 fn test_fold(cx: &mut gpui::MutableAppContext) {
6674 cx.set_global(Settings::test(cx));
6675 let buffer = MultiBuffer::build_simple(
6676 &"
6677 impl Foo {
6678 // Hello!
6679
6680 fn a() {
6681 1
6682 }
6683
6684 fn b() {
6685 2
6686 }
6687
6688 fn c() {
6689 3
6690 }
6691 }
6692 "
6693 .unindent(),
6694 cx,
6695 );
6696 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6697
6698 view.update(cx, |view, cx| {
6699 view.change_selections(None, cx, |s| {
6700 s.select_display_ranges([DisplayPoint::new(8, 0)..DisplayPoint::new(12, 0)]);
6701 });
6702 view.fold(&Fold, cx);
6703 assert_eq!(
6704 view.display_text(cx),
6705 "
6706 impl Foo {
6707 // Hello!
6708
6709 fn a() {
6710 1
6711 }
6712
6713 fn b() {…
6714 }
6715
6716 fn c() {…
6717 }
6718 }
6719 "
6720 .unindent(),
6721 );
6722
6723 view.fold(&Fold, cx);
6724 assert_eq!(
6725 view.display_text(cx),
6726 "
6727 impl Foo {…
6728 }
6729 "
6730 .unindent(),
6731 );
6732
6733 view.unfold_lines(&UnfoldLines, cx);
6734 assert_eq!(
6735 view.display_text(cx),
6736 "
6737 impl Foo {
6738 // Hello!
6739
6740 fn a() {
6741 1
6742 }
6743
6744 fn b() {…
6745 }
6746
6747 fn c() {…
6748 }
6749 }
6750 "
6751 .unindent(),
6752 );
6753
6754 view.unfold_lines(&UnfoldLines, cx);
6755 assert_eq!(view.display_text(cx), buffer.read(cx).read(cx).text());
6756 });
6757 }
6758
6759 #[gpui::test]
6760 fn test_move_cursor(cx: &mut gpui::MutableAppContext) {
6761 cx.set_global(Settings::test(cx));
6762 let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
6763 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6764
6765 buffer.update(cx, |buffer, cx| {
6766 buffer.edit(
6767 vec![
6768 (Point::new(1, 0)..Point::new(1, 0), "\t"),
6769 (Point::new(1, 1)..Point::new(1, 1), "\t"),
6770 ],
6771 cx,
6772 );
6773 });
6774
6775 view.update(cx, |view, cx| {
6776 assert_eq!(
6777 view.selections.display_ranges(cx),
6778 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6779 );
6780
6781 view.move_down(&MoveDown, cx);
6782 assert_eq!(
6783 view.selections.display_ranges(cx),
6784 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
6785 );
6786
6787 view.move_right(&MoveRight, cx);
6788 assert_eq!(
6789 view.selections.display_ranges(cx),
6790 &[DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4)]
6791 );
6792
6793 view.move_left(&MoveLeft, cx);
6794 assert_eq!(
6795 view.selections.display_ranges(cx),
6796 &[DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0)]
6797 );
6798
6799 view.move_up(&MoveUp, cx);
6800 assert_eq!(
6801 view.selections.display_ranges(cx),
6802 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6803 );
6804
6805 view.move_to_end(&MoveToEnd, cx);
6806 assert_eq!(
6807 view.selections.display_ranges(cx),
6808 &[DisplayPoint::new(5, 6)..DisplayPoint::new(5, 6)]
6809 );
6810
6811 view.move_to_beginning(&MoveToBeginning, cx);
6812 assert_eq!(
6813 view.selections.display_ranges(cx),
6814 &[DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)]
6815 );
6816
6817 view.change_selections(None, cx, |s| {
6818 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]);
6819 });
6820 view.select_to_beginning(&SelectToBeginning, cx);
6821 assert_eq!(
6822 view.selections.display_ranges(cx),
6823 &[DisplayPoint::new(0, 1)..DisplayPoint::new(0, 0)]
6824 );
6825
6826 view.select_to_end(&SelectToEnd, cx);
6827 assert_eq!(
6828 view.selections.display_ranges(cx),
6829 &[DisplayPoint::new(0, 1)..DisplayPoint::new(5, 6)]
6830 );
6831 });
6832 }
6833
6834 #[gpui::test]
6835 fn test_move_cursor_multibyte(cx: &mut gpui::MutableAppContext) {
6836 cx.set_global(Settings::test(cx));
6837 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
6838 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6839
6840 assert_eq!('ⓐ'.len_utf8(), 3);
6841 assert_eq!('α'.len_utf8(), 2);
6842
6843 view.update(cx, |view, cx| {
6844 view.fold_ranges(
6845 vec![
6846 Point::new(0, 6)..Point::new(0, 12),
6847 Point::new(1, 2)..Point::new(1, 4),
6848 Point::new(2, 4)..Point::new(2, 8),
6849 ],
6850 cx,
6851 );
6852 assert_eq!(view.display_text(cx), "ⓐⓑ…ⓔ\nab…e\nαβ…ε\n");
6853
6854 view.move_right(&MoveRight, cx);
6855 assert_eq!(
6856 view.selections.display_ranges(cx),
6857 &[empty_range(0, "ⓐ".len())]
6858 );
6859 view.move_right(&MoveRight, cx);
6860 assert_eq!(
6861 view.selections.display_ranges(cx),
6862 &[empty_range(0, "ⓐⓑ".len())]
6863 );
6864 view.move_right(&MoveRight, cx);
6865 assert_eq!(
6866 view.selections.display_ranges(cx),
6867 &[empty_range(0, "ⓐⓑ…".len())]
6868 );
6869
6870 view.move_down(&MoveDown, cx);
6871 assert_eq!(
6872 view.selections.display_ranges(cx),
6873 &[empty_range(1, "ab…".len())]
6874 );
6875 view.move_left(&MoveLeft, cx);
6876 assert_eq!(
6877 view.selections.display_ranges(cx),
6878 &[empty_range(1, "ab".len())]
6879 );
6880 view.move_left(&MoveLeft, cx);
6881 assert_eq!(
6882 view.selections.display_ranges(cx),
6883 &[empty_range(1, "a".len())]
6884 );
6885
6886 view.move_down(&MoveDown, cx);
6887 assert_eq!(
6888 view.selections.display_ranges(cx),
6889 &[empty_range(2, "α".len())]
6890 );
6891 view.move_right(&MoveRight, cx);
6892 assert_eq!(
6893 view.selections.display_ranges(cx),
6894 &[empty_range(2, "αβ".len())]
6895 );
6896 view.move_right(&MoveRight, cx);
6897 assert_eq!(
6898 view.selections.display_ranges(cx),
6899 &[empty_range(2, "αβ…".len())]
6900 );
6901 view.move_right(&MoveRight, cx);
6902 assert_eq!(
6903 view.selections.display_ranges(cx),
6904 &[empty_range(2, "αβ…ε".len())]
6905 );
6906
6907 view.move_up(&MoveUp, cx);
6908 assert_eq!(
6909 view.selections.display_ranges(cx),
6910 &[empty_range(1, "ab…e".len())]
6911 );
6912 view.move_up(&MoveUp, cx);
6913 assert_eq!(
6914 view.selections.display_ranges(cx),
6915 &[empty_range(0, "ⓐⓑ…ⓔ".len())]
6916 );
6917 view.move_left(&MoveLeft, cx);
6918 assert_eq!(
6919 view.selections.display_ranges(cx),
6920 &[empty_range(0, "ⓐⓑ…".len())]
6921 );
6922 view.move_left(&MoveLeft, cx);
6923 assert_eq!(
6924 view.selections.display_ranges(cx),
6925 &[empty_range(0, "ⓐⓑ".len())]
6926 );
6927 view.move_left(&MoveLeft, cx);
6928 assert_eq!(
6929 view.selections.display_ranges(cx),
6930 &[empty_range(0, "ⓐ".len())]
6931 );
6932 });
6933 }
6934
6935 #[gpui::test]
6936 fn test_move_cursor_different_line_lengths(cx: &mut gpui::MutableAppContext) {
6937 cx.set_global(Settings::test(cx));
6938 let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
6939 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
6940 view.update(cx, |view, cx| {
6941 view.change_selections(None, cx, |s| {
6942 s.select_display_ranges([empty_range(0, "ⓐⓑⓒⓓⓔ".len())]);
6943 });
6944 view.move_down(&MoveDown, cx);
6945 assert_eq!(
6946 view.selections.display_ranges(cx),
6947 &[empty_range(1, "abcd".len())]
6948 );
6949
6950 view.move_down(&MoveDown, cx);
6951 assert_eq!(
6952 view.selections.display_ranges(cx),
6953 &[empty_range(2, "αβγ".len())]
6954 );
6955
6956 view.move_down(&MoveDown, cx);
6957 assert_eq!(
6958 view.selections.display_ranges(cx),
6959 &[empty_range(3, "abcd".len())]
6960 );
6961
6962 view.move_down(&MoveDown, cx);
6963 assert_eq!(
6964 view.selections.display_ranges(cx),
6965 &[empty_range(4, "ⓐⓑⓒⓓⓔ".len())]
6966 );
6967
6968 view.move_up(&MoveUp, cx);
6969 assert_eq!(
6970 view.selections.display_ranges(cx),
6971 &[empty_range(3, "abcd".len())]
6972 );
6973
6974 view.move_up(&MoveUp, cx);
6975 assert_eq!(
6976 view.selections.display_ranges(cx),
6977 &[empty_range(2, "αβγ".len())]
6978 );
6979 });
6980 }
6981
6982 #[gpui::test]
6983 fn test_beginning_end_of_line(cx: &mut gpui::MutableAppContext) {
6984 cx.set_global(Settings::test(cx));
6985 let buffer = MultiBuffer::build_simple("abc\n def", cx);
6986 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
6987 view.update(cx, |view, cx| {
6988 view.change_selections(None, cx, |s| {
6989 s.select_display_ranges([
6990 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
6991 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
6992 ]);
6993 });
6994 });
6995
6996 view.update(cx, |view, cx| {
6997 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
6998 assert_eq!(
6999 view.selections.display_ranges(cx),
7000 &[
7001 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7002 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7003 ]
7004 );
7005 });
7006
7007 view.update(cx, |view, cx| {
7008 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7009 assert_eq!(
7010 view.selections.display_ranges(cx),
7011 &[
7012 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7013 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7014 ]
7015 );
7016 });
7017
7018 view.update(cx, |view, cx| {
7019 view.move_to_beginning_of_line(&MoveToBeginningOfLine, cx);
7020 assert_eq!(
7021 view.selections.display_ranges(cx),
7022 &[
7023 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7024 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7025 ]
7026 );
7027 });
7028
7029 view.update(cx, |view, cx| {
7030 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7031 assert_eq!(
7032 view.selections.display_ranges(cx),
7033 &[
7034 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7035 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7036 ]
7037 );
7038 });
7039
7040 // Moving to the end of line again is a no-op.
7041 view.update(cx, |view, cx| {
7042 view.move_to_end_of_line(&MoveToEndOfLine, cx);
7043 assert_eq!(
7044 view.selections.display_ranges(cx),
7045 &[
7046 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7047 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
7048 ]
7049 );
7050 });
7051
7052 view.update(cx, |view, cx| {
7053 view.move_left(&MoveLeft, cx);
7054 view.select_to_beginning_of_line(
7055 &SelectToBeginningOfLine {
7056 stop_at_soft_wraps: true,
7057 },
7058 cx,
7059 );
7060 assert_eq!(
7061 view.selections.display_ranges(cx),
7062 &[
7063 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7064 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7065 ]
7066 );
7067 });
7068
7069 view.update(cx, |view, cx| {
7070 view.select_to_beginning_of_line(
7071 &SelectToBeginningOfLine {
7072 stop_at_soft_wraps: true,
7073 },
7074 cx,
7075 );
7076 assert_eq!(
7077 view.selections.display_ranges(cx),
7078 &[
7079 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7080 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 0),
7081 ]
7082 );
7083 });
7084
7085 view.update(cx, |view, cx| {
7086 view.select_to_beginning_of_line(
7087 &SelectToBeginningOfLine {
7088 stop_at_soft_wraps: true,
7089 },
7090 cx,
7091 );
7092 assert_eq!(
7093 view.selections.display_ranges(cx),
7094 &[
7095 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 0),
7096 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 2),
7097 ]
7098 );
7099 });
7100
7101 view.update(cx, |view, cx| {
7102 view.select_to_end_of_line(
7103 &SelectToEndOfLine {
7104 stop_at_soft_wraps: true,
7105 },
7106 cx,
7107 );
7108 assert_eq!(
7109 view.selections.display_ranges(cx),
7110 &[
7111 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
7112 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 5),
7113 ]
7114 );
7115 });
7116
7117 view.update(cx, |view, cx| {
7118 view.delete_to_end_of_line(&DeleteToEndOfLine, cx);
7119 assert_eq!(view.display_text(cx), "ab\n de");
7120 assert_eq!(
7121 view.selections.display_ranges(cx),
7122 &[
7123 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7124 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 4),
7125 ]
7126 );
7127 });
7128
7129 view.update(cx, |view, cx| {
7130 view.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7131 assert_eq!(view.display_text(cx), "\n");
7132 assert_eq!(
7133 view.selections.display_ranges(cx),
7134 &[
7135 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7136 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7137 ]
7138 );
7139 });
7140 }
7141
7142 #[gpui::test]
7143 fn test_prev_next_word_boundary(cx: &mut gpui::MutableAppContext) {
7144 cx.set_global(Settings::test(cx));
7145 let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
7146 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7147 view.update(cx, |view, cx| {
7148 view.change_selections(None, cx, |s| {
7149 s.select_display_ranges([
7150 DisplayPoint::new(0, 11)..DisplayPoint::new(0, 11),
7151 DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4),
7152 ])
7153 });
7154
7155 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7156 assert_selection_ranges(
7157 "use std::<>str::{foo, bar}\n\n {[]baz.qux()}",
7158 vec![('<', '>'), ('[', ']')],
7159 view,
7160 cx,
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_next_word_end(&MoveToNextWordEnd, 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_right(&MoveRight, cx);
7220 view.select_to_previous_word_start(&SelectToPreviousWordStart, cx);
7221 assert_selection_ranges(
7222 "use std::>s<tr::{foo, bar}\n\n {]b[az.qux()}",
7223 vec![('<', '>'), ('[', ']')],
7224 view,
7225 cx,
7226 );
7227
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_next_word_end(&SelectToNextWordEnd, 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 }
7245
7246 #[gpui::test]
7247 fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut gpui::MutableAppContext) {
7248 cx.set_global(Settings::test(cx));
7249 let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
7250 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7251
7252 view.update(cx, |view, cx| {
7253 view.set_wrap_width(Some(140.), cx);
7254 assert_eq!(
7255 view.display_text(cx),
7256 "use one::{\n two::three::\n four::five\n};"
7257 );
7258
7259 view.change_selections(None, cx, |s| {
7260 s.select_display_ranges([DisplayPoint::new(1, 7)..DisplayPoint::new(1, 7)]);
7261 });
7262
7263 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7264 assert_eq!(
7265 view.selections.display_ranges(cx),
7266 &[DisplayPoint::new(1, 9)..DisplayPoint::new(1, 9)]
7267 );
7268
7269 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7270 assert_eq!(
7271 view.selections.display_ranges(cx),
7272 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7273 );
7274
7275 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7276 assert_eq!(
7277 view.selections.display_ranges(cx),
7278 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7279 );
7280
7281 view.move_to_next_word_end(&MoveToNextWordEnd, cx);
7282 assert_eq!(
7283 view.selections.display_ranges(cx),
7284 &[DisplayPoint::new(2, 8)..DisplayPoint::new(2, 8)]
7285 );
7286
7287 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7288 assert_eq!(
7289 view.selections.display_ranges(cx),
7290 &[DisplayPoint::new(2, 4)..DisplayPoint::new(2, 4)]
7291 );
7292
7293 view.move_to_previous_word_start(&MoveToPreviousWordStart, cx);
7294 assert_eq!(
7295 view.selections.display_ranges(cx),
7296 &[DisplayPoint::new(1, 14)..DisplayPoint::new(1, 14)]
7297 );
7298 });
7299 }
7300
7301 #[gpui::test]
7302 fn test_delete_to_beginning_of_line(cx: &mut gpui::MutableAppContext) {
7303 cx.set_global(Settings::test(cx));
7304 let (text, ranges) = marked_text_ranges("one [two three] four");
7305 let buffer = MultiBuffer::build_simple(&text, cx);
7306
7307 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7308
7309 editor.update(cx, |editor, cx| {
7310 editor.change_selections(None, cx, |s| s.select_ranges(ranges));
7311 editor.delete_to_beginning_of_line(&DeleteToBeginningOfLine, cx);
7312 assert_eq!(editor.text(cx), " four");
7313 });
7314 }
7315
7316 #[gpui::test]
7317 fn test_delete_to_word_boundary(cx: &mut gpui::MutableAppContext) {
7318 cx.set_global(Settings::test(cx));
7319 let buffer = MultiBuffer::build_simple("one two three four", cx);
7320 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7321
7322 view.update(cx, |view, cx| {
7323 view.change_selections(None, cx, |s| {
7324 s.select_display_ranges([
7325 // an empty selection - the preceding word fragment is deleted
7326 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7327 // characters selected - they are deleted
7328 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 12),
7329 ])
7330 });
7331 view.delete_to_previous_word_start(&DeleteToPreviousWordStart, cx);
7332 });
7333
7334 assert_eq!(buffer.read(cx).read(cx).text(), "e two te four");
7335
7336 view.update(cx, |view, cx| {
7337 view.change_selections(None, cx, |s| {
7338 s.select_display_ranges([
7339 // an empty selection - the following word fragment is deleted
7340 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
7341 // characters selected - they are deleted
7342 DisplayPoint::new(0, 9)..DisplayPoint::new(0, 10),
7343 ])
7344 });
7345 view.delete_to_next_word_end(&DeleteToNextWordEnd, cx);
7346 });
7347
7348 assert_eq!(buffer.read(cx).read(cx).text(), "e t te our");
7349 }
7350
7351 #[gpui::test]
7352 fn test_newline(cx: &mut gpui::MutableAppContext) {
7353 cx.set_global(Settings::test(cx));
7354 let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
7355 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
7356
7357 view.update(cx, |view, cx| {
7358 view.change_selections(None, cx, |s| {
7359 s.select_display_ranges([
7360 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7361 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7362 DisplayPoint::new(1, 6)..DisplayPoint::new(1, 6),
7363 ])
7364 });
7365
7366 view.newline(&Newline, cx);
7367 assert_eq!(view.text(cx), "aa\naa\n \n bb\n bb\n");
7368 });
7369 }
7370
7371 #[gpui::test]
7372 fn test_newline_with_old_selections(cx: &mut gpui::MutableAppContext) {
7373 cx.set_global(Settings::test(cx));
7374 let buffer = MultiBuffer::build_simple(
7375 "
7376 a
7377 b(
7378 X
7379 )
7380 c(
7381 X
7382 )
7383 "
7384 .unindent()
7385 .as_str(),
7386 cx,
7387 );
7388
7389 let (_, editor) = cx.add_window(Default::default(), |cx| {
7390 let mut editor = build_editor(buffer.clone(), cx);
7391 editor.change_selections(None, cx, |s| {
7392 s.select_ranges([
7393 Point::new(2, 4)..Point::new(2, 5),
7394 Point::new(5, 4)..Point::new(5, 5),
7395 ])
7396 });
7397 editor
7398 });
7399
7400 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7401 buffer.update(cx, |buffer, cx| {
7402 buffer.edit(
7403 [
7404 (Point::new(1, 2)..Point::new(3, 0), ""),
7405 (Point::new(4, 2)..Point::new(6, 0), ""),
7406 ],
7407 cx,
7408 );
7409 assert_eq!(
7410 buffer.read(cx).text(),
7411 "
7412 a
7413 b()
7414 c()
7415 "
7416 .unindent()
7417 );
7418 });
7419
7420 editor.update(cx, |editor, cx| {
7421 assert_eq!(
7422 editor.selections.ranges(cx),
7423 &[
7424 Point::new(1, 2)..Point::new(1, 2),
7425 Point::new(2, 2)..Point::new(2, 2),
7426 ],
7427 );
7428
7429 editor.newline(&Newline, cx);
7430 assert_eq!(
7431 editor.text(cx),
7432 "
7433 a
7434 b(
7435 )
7436 c(
7437 )
7438 "
7439 .unindent()
7440 );
7441
7442 // The selections are moved after the inserted newlines
7443 assert_eq!(
7444 editor.selections.ranges(cx),
7445 &[
7446 Point::new(2, 0)..Point::new(2, 0),
7447 Point::new(4, 0)..Point::new(4, 0),
7448 ],
7449 );
7450 });
7451 }
7452
7453 #[gpui::test]
7454 fn test_insert_with_old_selections(cx: &mut gpui::MutableAppContext) {
7455 cx.set_global(Settings::test(cx));
7456 let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
7457 let (_, editor) = cx.add_window(Default::default(), |cx| {
7458 let mut editor = build_editor(buffer.clone(), cx);
7459 editor.change_selections(None, cx, |s| s.select_ranges([3..4, 11..12, 19..20]));
7460 editor
7461 });
7462
7463 // Edit the buffer directly, deleting ranges surrounding the editor's selections
7464 buffer.update(cx, |buffer, cx| {
7465 buffer.edit([(2..5, ""), (10..13, ""), (18..21, "")], cx);
7466 assert_eq!(buffer.read(cx).text(), "a(), b(), c()".unindent());
7467 });
7468
7469 editor.update(cx, |editor, cx| {
7470 assert_eq!(editor.selections.ranges(cx), &[2..2, 7..7, 12..12],);
7471
7472 editor.insert("Z", cx);
7473 assert_eq!(editor.text(cx), "a(Z), b(Z), c(Z)");
7474
7475 // The selections are moved after the inserted characters
7476 assert_eq!(editor.selections.ranges(cx), &[3..3, 9..9, 15..15],);
7477 });
7478 }
7479
7480 #[gpui::test]
7481 async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
7482 let mut cx = EditorTestContext::new(cx).await;
7483
7484 cx.set_state(indoc! {"
7485 [one} [two}
7486 three
7487 four"});
7488 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7489 cx.assert_editor_state(indoc! {"
7490 [one} [two}
7491 three
7492 four"});
7493
7494 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7495 cx.assert_editor_state(indoc! {"
7496 [one} [two}
7497 three
7498 four"});
7499
7500 // select across line ending
7501 cx.set_state(indoc! {"
7502 one two
7503 t[hree
7504 } four"});
7505 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7506 cx.assert_editor_state(indoc! {"
7507 one two
7508 t[hree
7509 } four"});
7510
7511 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7512 cx.assert_editor_state(indoc! {"
7513 one two
7514 t[hree
7515 } four"});
7516
7517 // Ensure that indenting/outdenting works when the cursor is at column 0.
7518 cx.set_state(indoc! {"
7519 one two
7520 |three
7521 four"});
7522 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7523 cx.assert_editor_state(indoc! {"
7524 one two
7525 |three
7526 four"});
7527
7528 cx.set_state(indoc! {"
7529 one two
7530 | three
7531 four"});
7532 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7533 cx.assert_editor_state(indoc! {"
7534 one two
7535 |three
7536 four"});
7537 }
7538
7539 #[gpui::test]
7540 async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
7541 let mut cx = EditorTestContext::new(cx).await;
7542 cx.update(|cx| {
7543 cx.update_global::<Settings, _, _>(|settings, _| {
7544 settings.language_settings.hard_tabs = Some(true);
7545 });
7546 });
7547
7548 // select two ranges on one line
7549 cx.set_state(indoc! {"
7550 [one} [two}
7551 three
7552 four"});
7553 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7554 cx.assert_editor_state(indoc! {"
7555 \t[one} [two}
7556 three
7557 four"});
7558 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7559 cx.assert_editor_state(indoc! {"
7560 \t\t[one} [two}
7561 three
7562 four"});
7563 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7564 cx.assert_editor_state(indoc! {"
7565 \t[one} [two}
7566 three
7567 four"});
7568 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7569 cx.assert_editor_state(indoc! {"
7570 [one} [two}
7571 three
7572 four"});
7573
7574 // select across a line ending
7575 cx.set_state(indoc! {"
7576 one two
7577 t[hree
7578 }four"});
7579 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7580 cx.assert_editor_state(indoc! {"
7581 one two
7582 \tt[hree
7583 }four"});
7584 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7585 cx.assert_editor_state(indoc! {"
7586 one two
7587 \t\tt[hree
7588 }four"});
7589 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7590 cx.assert_editor_state(indoc! {"
7591 one two
7592 \tt[hree
7593 }four"});
7594 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7595 cx.assert_editor_state(indoc! {"
7596 one two
7597 t[hree
7598 }four"});
7599
7600 // Ensure that indenting/outdenting works when the cursor is at column 0.
7601 cx.set_state(indoc! {"
7602 one two
7603 |three
7604 four"});
7605 cx.assert_editor_state(indoc! {"
7606 one two
7607 |three
7608 four"});
7609 cx.update_editor(|e, cx| e.tab(&Tab, cx));
7610 cx.assert_editor_state(indoc! {"
7611 one two
7612 \t|three
7613 four"});
7614 cx.update_editor(|e, cx| e.tab_prev(&TabPrev, cx));
7615 cx.assert_editor_state(indoc! {"
7616 one two
7617 |three
7618 four"});
7619 }
7620
7621 #[gpui::test]
7622 fn test_indent_outdent_with_excerpts(cx: &mut gpui::MutableAppContext) {
7623 cx.set_global(
7624 Settings::test(cx)
7625 .with_language_defaults(
7626 "TOML",
7627 LanguageSettings {
7628 tab_size: Some(2.try_into().unwrap()),
7629 ..Default::default()
7630 },
7631 )
7632 .with_language_defaults(
7633 "Rust",
7634 LanguageSettings {
7635 tab_size: Some(4.try_into().unwrap()),
7636 ..Default::default()
7637 },
7638 ),
7639 );
7640 let toml_language = Arc::new(Language::new(
7641 LanguageConfig {
7642 name: "TOML".into(),
7643 ..Default::default()
7644 },
7645 None,
7646 ));
7647 let rust_language = Arc::new(Language::new(
7648 LanguageConfig {
7649 name: "Rust".into(),
7650 ..Default::default()
7651 },
7652 None,
7653 ));
7654
7655 let toml_buffer = cx
7656 .add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx).with_language(toml_language, cx));
7657 let rust_buffer = cx.add_model(|cx| {
7658 Buffer::new(0, "const c: usize = 3;\n", cx).with_language(rust_language, cx)
7659 });
7660 let multibuffer = cx.add_model(|cx| {
7661 let mut multibuffer = MultiBuffer::new(0);
7662 multibuffer.push_excerpts(
7663 toml_buffer.clone(),
7664 [ExcerptRange {
7665 context: Point::new(0, 0)..Point::new(2, 0),
7666 primary: None,
7667 }],
7668 cx,
7669 );
7670 multibuffer.push_excerpts(
7671 rust_buffer.clone(),
7672 [ExcerptRange {
7673 context: Point::new(0, 0)..Point::new(1, 0),
7674 primary: None,
7675 }],
7676 cx,
7677 );
7678 multibuffer
7679 });
7680
7681 cx.add_window(Default::default(), |cx| {
7682 let mut editor = build_editor(multibuffer, cx);
7683
7684 assert_eq!(
7685 editor.text(cx),
7686 indoc! {"
7687 a = 1
7688 b = 2
7689
7690 const c: usize = 3;
7691 "}
7692 );
7693
7694 select_ranges(
7695 &mut editor,
7696 indoc! {"
7697 [a] = 1
7698 b = 2
7699
7700 [const c:] usize = 3;
7701 "},
7702 cx,
7703 );
7704
7705 editor.tab(&Tab, cx);
7706 assert_text_with_selections(
7707 &mut editor,
7708 indoc! {"
7709 [a] = 1
7710 b = 2
7711
7712 [const c:] usize = 3;
7713 "},
7714 cx,
7715 );
7716 editor.tab_prev(&TabPrev, cx);
7717 assert_text_with_selections(
7718 &mut editor,
7719 indoc! {"
7720 [a] = 1
7721 b = 2
7722
7723 [const c:] usize = 3;
7724 "},
7725 cx,
7726 );
7727
7728 editor
7729 });
7730 }
7731
7732 #[gpui::test]
7733 async fn test_backspace(cx: &mut gpui::TestAppContext) {
7734 let mut cx = EditorTestContext::new(cx).await;
7735 // Basic backspace
7736 cx.set_state(indoc! {"
7737 on|e two three
7738 fou[r} five six
7739 seven {eight nine
7740 ]ten"});
7741 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7742 cx.assert_editor_state(indoc! {"
7743 o|e two three
7744 fou| five six
7745 seven |ten"});
7746
7747 // Test backspace inside and around indents
7748 cx.set_state(indoc! {"
7749 zero
7750 |one
7751 |two
7752 | | | three
7753 | | four"});
7754 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7755 cx.assert_editor_state(indoc! {"
7756 zero
7757 |one
7758 |two
7759 | three| four"});
7760
7761 // Test backspace with line_mode set to true
7762 cx.update_editor(|e, _| e.selections.line_mode = true);
7763 cx.set_state(indoc! {"
7764 The |quick |brown
7765 fox jumps over
7766 the lazy dog
7767 |The qu[ick b}rown"});
7768 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7769 cx.assert_editor_state(indoc! {"
7770 |fox jumps over
7771 the lazy dog|"});
7772 }
7773
7774 #[gpui::test]
7775 async fn test_delete(cx: &mut gpui::TestAppContext) {
7776 let mut cx = EditorTestContext::new(cx).await;
7777
7778 cx.set_state(indoc! {"
7779 on|e two three
7780 fou[r} five six
7781 seven {eight nine
7782 ]ten"});
7783 cx.update_editor(|e, cx| e.delete(&Delete, cx));
7784 cx.assert_editor_state(indoc! {"
7785 on| two three
7786 fou| five six
7787 seven |ten"});
7788
7789 // Test backspace with line_mode set to true
7790 cx.update_editor(|e, _| e.selections.line_mode = true);
7791 cx.set_state(indoc! {"
7792 The |quick |brown
7793 fox {jum]ps over
7794 the lazy dog
7795 |The qu[ick b}rown"});
7796 cx.update_editor(|e, cx| e.backspace(&Backspace, cx));
7797 cx.assert_editor_state("|the lazy dog|");
7798 }
7799
7800 #[gpui::test]
7801 fn test_delete_line(cx: &mut gpui::MutableAppContext) {
7802 cx.set_global(Settings::test(cx));
7803 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7804 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7805 view.update(cx, |view, cx| {
7806 view.change_selections(None, cx, |s| {
7807 s.select_display_ranges([
7808 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7809 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
7810 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7811 ])
7812 });
7813 view.delete_line(&DeleteLine, cx);
7814 assert_eq!(view.display_text(cx), "ghi");
7815 assert_eq!(
7816 view.selections.display_ranges(cx),
7817 vec![
7818 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0),
7819 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)
7820 ]
7821 );
7822 });
7823
7824 cx.set_global(Settings::test(cx));
7825 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7826 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7827 view.update(cx, |view, cx| {
7828 view.change_selections(None, cx, |s| {
7829 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(0, 1)])
7830 });
7831 view.delete_line(&DeleteLine, cx);
7832 assert_eq!(view.display_text(cx), "ghi\n");
7833 assert_eq!(
7834 view.selections.display_ranges(cx),
7835 vec![DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)]
7836 );
7837 });
7838 }
7839
7840 #[gpui::test]
7841 fn test_duplicate_line(cx: &mut gpui::MutableAppContext) {
7842 cx.set_global(Settings::test(cx));
7843 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7844 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7845 view.update(cx, |view, cx| {
7846 view.change_selections(None, cx, |s| {
7847 s.select_display_ranges([
7848 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
7849 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
7850 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
7851 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7852 ])
7853 });
7854 view.duplicate_line(&DuplicateLine, cx);
7855 assert_eq!(view.display_text(cx), "abc\nabc\ndef\ndef\nghi\n\n");
7856 assert_eq!(
7857 view.selections.display_ranges(cx),
7858 vec![
7859 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
7860 DisplayPoint::new(1, 2)..DisplayPoint::new(1, 2),
7861 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
7862 DisplayPoint::new(6, 0)..DisplayPoint::new(6, 0),
7863 ]
7864 );
7865 });
7866
7867 let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
7868 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7869 view.update(cx, |view, cx| {
7870 view.change_selections(None, cx, |s| {
7871 s.select_display_ranges([
7872 DisplayPoint::new(0, 1)..DisplayPoint::new(1, 1),
7873 DisplayPoint::new(1, 2)..DisplayPoint::new(2, 1),
7874 ])
7875 });
7876 view.duplicate_line(&DuplicateLine, cx);
7877 assert_eq!(view.display_text(cx), "abc\ndef\nghi\nabc\ndef\nghi\n");
7878 assert_eq!(
7879 view.selections.display_ranges(cx),
7880 vec![
7881 DisplayPoint::new(3, 1)..DisplayPoint::new(4, 1),
7882 DisplayPoint::new(4, 2)..DisplayPoint::new(5, 1),
7883 ]
7884 );
7885 });
7886 }
7887
7888 #[gpui::test]
7889 fn test_move_line_up_down(cx: &mut gpui::MutableAppContext) {
7890 cx.set_global(Settings::test(cx));
7891 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
7892 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7893 view.update(cx, |view, cx| {
7894 view.fold_ranges(
7895 vec![
7896 Point::new(0, 2)..Point::new(1, 2),
7897 Point::new(2, 3)..Point::new(4, 1),
7898 Point::new(7, 0)..Point::new(8, 4),
7899 ],
7900 cx,
7901 );
7902 view.change_selections(None, cx, |s| {
7903 s.select_display_ranges([
7904 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7905 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
7906 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
7907 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2),
7908 ])
7909 });
7910 assert_eq!(
7911 view.display_text(cx),
7912 "aa…bbb\nccc…eeee\nfffff\nggggg\n…i\njjjjj"
7913 );
7914
7915 view.move_line_up(&MoveLineUp, cx);
7916 assert_eq!(
7917 view.display_text(cx),
7918 "aa…bbb\nccc…eeee\nggggg\n…i\njjjjj\nfffff"
7919 );
7920 assert_eq!(
7921 view.selections.display_ranges(cx),
7922 vec![
7923 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
7924 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
7925 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
7926 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
7927 ]
7928 );
7929 });
7930
7931 view.update(cx, |view, cx| {
7932 view.move_line_down(&MoveLineDown, cx);
7933 assert_eq!(
7934 view.display_text(cx),
7935 "ccc…eeee\naa…bbb\nfffff\nggggg\n…i\njjjjj"
7936 );
7937 assert_eq!(
7938 view.selections.display_ranges(cx),
7939 vec![
7940 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
7941 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
7942 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
7943 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
7944 ]
7945 );
7946 });
7947
7948 view.update(cx, |view, cx| {
7949 view.move_line_down(&MoveLineDown, cx);
7950 assert_eq!(
7951 view.display_text(cx),
7952 "ccc…eeee\nfffff\naa…bbb\nggggg\n…i\njjjjj"
7953 );
7954 assert_eq!(
7955 view.selections.display_ranges(cx),
7956 vec![
7957 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
7958 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 1),
7959 DisplayPoint::new(3, 2)..DisplayPoint::new(4, 3),
7960 DisplayPoint::new(5, 0)..DisplayPoint::new(5, 2)
7961 ]
7962 );
7963 });
7964
7965 view.update(cx, |view, cx| {
7966 view.move_line_up(&MoveLineUp, cx);
7967 assert_eq!(
7968 view.display_text(cx),
7969 "ccc…eeee\naa…bbb\nggggg\n…i\njjjjj\nfffff"
7970 );
7971 assert_eq!(
7972 view.selections.display_ranges(cx),
7973 vec![
7974 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 1),
7975 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
7976 DisplayPoint::new(2, 2)..DisplayPoint::new(3, 3),
7977 DisplayPoint::new(4, 0)..DisplayPoint::new(4, 2)
7978 ]
7979 );
7980 });
7981 }
7982
7983 #[gpui::test]
7984 fn test_move_line_up_down_with_blocks(cx: &mut gpui::MutableAppContext) {
7985 cx.set_global(Settings::test(cx));
7986 let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
7987 let snapshot = buffer.read(cx).snapshot(cx);
7988 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
7989 editor.update(cx, |editor, cx| {
7990 editor.insert_blocks(
7991 [BlockProperties {
7992 style: BlockStyle::Fixed,
7993 position: snapshot.anchor_after(Point::new(2, 0)),
7994 disposition: BlockDisposition::Below,
7995 height: 1,
7996 render: Arc::new(|_| Empty::new().boxed()),
7997 }],
7998 cx,
7999 );
8000 editor.change_selections(None, cx, |s| {
8001 s.select_ranges([Point::new(2, 0)..Point::new(2, 0)])
8002 });
8003 editor.move_line_down(&MoveLineDown, cx);
8004 });
8005 }
8006
8007 #[gpui::test]
8008 fn test_transpose(cx: &mut gpui::MutableAppContext) {
8009 cx.set_global(Settings::test(cx));
8010
8011 cx.add_window(Default::default(), |cx| {
8012 let mut editor = build_editor(MultiBuffer::build_simple("abc", cx), cx);
8013
8014 editor.change_selections(None, cx, |s| s.select_ranges([1..1]));
8015 editor.transpose(&Default::default(), cx);
8016 assert_eq!(editor.text(cx), "bac");
8017 assert_eq!(editor.selections.ranges(cx), [2..2]);
8018
8019 editor.transpose(&Default::default(), cx);
8020 assert_eq!(editor.text(cx), "bca");
8021 assert_eq!(editor.selections.ranges(cx), [3..3]);
8022
8023 editor.transpose(&Default::default(), cx);
8024 assert_eq!(editor.text(cx), "bac");
8025 assert_eq!(editor.selections.ranges(cx), [3..3]);
8026
8027 editor
8028 })
8029 .1;
8030
8031 cx.add_window(Default::default(), |cx| {
8032 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8033
8034 editor.change_selections(None, cx, |s| s.select_ranges([3..3]));
8035 editor.transpose(&Default::default(), cx);
8036 assert_eq!(editor.text(cx), "acb\nde");
8037 assert_eq!(editor.selections.ranges(cx), [3..3]);
8038
8039 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8040 editor.transpose(&Default::default(), cx);
8041 assert_eq!(editor.text(cx), "acbd\ne");
8042 assert_eq!(editor.selections.ranges(cx), [5..5]);
8043
8044 editor.transpose(&Default::default(), cx);
8045 assert_eq!(editor.text(cx), "acbde\n");
8046 assert_eq!(editor.selections.ranges(cx), [6..6]);
8047
8048 editor.transpose(&Default::default(), cx);
8049 assert_eq!(editor.text(cx), "acbd\ne");
8050 assert_eq!(editor.selections.ranges(cx), [6..6]);
8051
8052 editor
8053 })
8054 .1;
8055
8056 cx.add_window(Default::default(), |cx| {
8057 let mut editor = build_editor(MultiBuffer::build_simple("abc\nde", cx), cx);
8058
8059 editor.change_selections(None, cx, |s| s.select_ranges([1..1, 2..2, 4..4]));
8060 editor.transpose(&Default::default(), cx);
8061 assert_eq!(editor.text(cx), "bacd\ne");
8062 assert_eq!(editor.selections.ranges(cx), [2..2, 3..3, 5..5]);
8063
8064 editor.transpose(&Default::default(), cx);
8065 assert_eq!(editor.text(cx), "bcade\n");
8066 assert_eq!(editor.selections.ranges(cx), [3..3, 4..4, 6..6]);
8067
8068 editor.transpose(&Default::default(), cx);
8069 assert_eq!(editor.text(cx), "bcda\ne");
8070 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8071
8072 editor.transpose(&Default::default(), cx);
8073 assert_eq!(editor.text(cx), "bcade\n");
8074 assert_eq!(editor.selections.ranges(cx), [4..4, 6..6]);
8075
8076 editor.transpose(&Default::default(), cx);
8077 assert_eq!(editor.text(cx), "bcaed\n");
8078 assert_eq!(editor.selections.ranges(cx), [5..5, 6..6]);
8079
8080 editor
8081 })
8082 .1;
8083
8084 cx.add_window(Default::default(), |cx| {
8085 let mut editor = build_editor(MultiBuffer::build_simple("🍐🏀✋", cx), cx);
8086
8087 editor.change_selections(None, cx, |s| s.select_ranges([4..4]));
8088 editor.transpose(&Default::default(), cx);
8089 assert_eq!(editor.text(cx), "🏀🍐✋");
8090 assert_eq!(editor.selections.ranges(cx), [8..8]);
8091
8092 editor.transpose(&Default::default(), cx);
8093 assert_eq!(editor.text(cx), "🏀✋🍐");
8094 assert_eq!(editor.selections.ranges(cx), [11..11]);
8095
8096 editor.transpose(&Default::default(), cx);
8097 assert_eq!(editor.text(cx), "🏀🍐✋");
8098 assert_eq!(editor.selections.ranges(cx), [11..11]);
8099
8100 editor
8101 })
8102 .1;
8103 }
8104
8105 #[gpui::test]
8106 async fn test_clipboard(cx: &mut gpui::TestAppContext) {
8107 let mut cx = EditorTestContext::new(cx).await;
8108
8109 cx.set_state("[one✅ }two [three }four [five }six ");
8110 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8111 cx.assert_editor_state("|two |four |six ");
8112
8113 // Paste with three cursors. Each cursor pastes one slice of the clipboard text.
8114 cx.set_state("two |four |six |");
8115 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8116 cx.assert_editor_state("two one✅ |four three |six five |");
8117
8118 // Paste again but with only two cursors. Since the number of cursors doesn't
8119 // match the number of slices in the clipboard, the entire clipboard text
8120 // is pasted at each cursor.
8121 cx.set_state("|two one✅ four three six five |");
8122 cx.update_editor(|e, cx| {
8123 e.handle_input(&Input("( ".into()), cx);
8124 e.paste(&Paste, cx);
8125 e.handle_input(&Input(") ".into()), cx);
8126 });
8127 cx.assert_editor_state(indoc! {"
8128 ( one✅
8129 three
8130 five ) |two one✅ four three six five ( one✅
8131 three
8132 five ) |"});
8133
8134 // Cut with three selections, one of which is full-line.
8135 cx.set_state(indoc! {"
8136 1[2}3
8137 4|567
8138 [8}9"});
8139 cx.update_editor(|e, cx| e.cut(&Cut, cx));
8140 cx.assert_editor_state(indoc! {"
8141 1|3
8142 |9"});
8143
8144 // Paste with three selections, noticing how the copied selection that was full-line
8145 // gets inserted before the second cursor.
8146 cx.set_state(indoc! {"
8147 1|3
8148 9|
8149 [o}ne"});
8150 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8151 cx.assert_editor_state(indoc! {"
8152 12|3
8153 4567
8154 9|
8155 8|ne"});
8156
8157 // Copy with a single cursor only, which writes the whole line into the clipboard.
8158 cx.set_state(indoc! {"
8159 The quick brown
8160 fox ju|mps over
8161 the lazy dog"});
8162 cx.update_editor(|e, cx| e.copy(&Copy, cx));
8163 cx.assert_clipboard_content(Some("fox jumps over\n"));
8164
8165 // Paste with three selections, noticing how the copied full-line selection is inserted
8166 // before the empty selections but replaces the selection that is non-empty.
8167 cx.set_state(indoc! {"
8168 T|he quick brown
8169 [fo}x jumps over
8170 t|he lazy dog"});
8171 cx.update_editor(|e, cx| e.paste(&Paste, cx));
8172 cx.assert_editor_state(indoc! {"
8173 fox jumps over
8174 T|he quick brown
8175 fox jumps over
8176 |x jumps over
8177 fox jumps over
8178 t|he lazy dog"});
8179 }
8180
8181 #[gpui::test]
8182 fn test_select_all(cx: &mut gpui::MutableAppContext) {
8183 cx.set_global(Settings::test(cx));
8184 let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
8185 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8186 view.update(cx, |view, cx| {
8187 view.select_all(&SelectAll, cx);
8188 assert_eq!(
8189 view.selections.display_ranges(cx),
8190 &[DisplayPoint::new(0, 0)..DisplayPoint::new(2, 3)]
8191 );
8192 });
8193 }
8194
8195 #[gpui::test]
8196 fn test_select_line(cx: &mut gpui::MutableAppContext) {
8197 cx.set_global(Settings::test(cx));
8198 let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
8199 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8200 view.update(cx, |view, cx| {
8201 view.change_selections(None, cx, |s| {
8202 s.select_display_ranges([
8203 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8204 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8205 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8206 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 2),
8207 ])
8208 });
8209 view.select_line(&SelectLine, cx);
8210 assert_eq!(
8211 view.selections.display_ranges(cx),
8212 vec![
8213 DisplayPoint::new(0, 0)..DisplayPoint::new(2, 0),
8214 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 0),
8215 ]
8216 );
8217 });
8218
8219 view.update(cx, |view, cx| {
8220 view.select_line(&SelectLine, cx);
8221 assert_eq!(
8222 view.selections.display_ranges(cx),
8223 vec![
8224 DisplayPoint::new(0, 0)..DisplayPoint::new(3, 0),
8225 DisplayPoint::new(4, 0)..DisplayPoint::new(5, 5),
8226 ]
8227 );
8228 });
8229
8230 view.update(cx, |view, cx| {
8231 view.select_line(&SelectLine, cx);
8232 assert_eq!(
8233 view.selections.display_ranges(cx),
8234 vec![DisplayPoint::new(0, 0)..DisplayPoint::new(5, 5)]
8235 );
8236 });
8237 }
8238
8239 #[gpui::test]
8240 fn test_split_selection_into_lines(cx: &mut gpui::MutableAppContext) {
8241 cx.set_global(Settings::test(cx));
8242 let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
8243 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8244 view.update(cx, |view, cx| {
8245 view.fold_ranges(
8246 vec![
8247 Point::new(0, 2)..Point::new(1, 2),
8248 Point::new(2, 3)..Point::new(4, 1),
8249 Point::new(7, 0)..Point::new(8, 4),
8250 ],
8251 cx,
8252 );
8253 view.change_selections(None, cx, |s| {
8254 s.select_display_ranges([
8255 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8256 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8257 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8258 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
8259 ])
8260 });
8261 assert_eq!(view.display_text(cx), "aa…bbb\nccc…eeee\nfffff\nggggg\n…i");
8262 });
8263
8264 view.update(cx, |view, cx| {
8265 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8266 assert_eq!(
8267 view.display_text(cx),
8268 "aaaaa\nbbbbb\nccc…eeee\nfffff\nggggg\n…i"
8269 );
8270 assert_eq!(
8271 view.selections.display_ranges(cx),
8272 [
8273 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1),
8274 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2),
8275 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 0),
8276 DisplayPoint::new(5, 4)..DisplayPoint::new(5, 4)
8277 ]
8278 );
8279 });
8280
8281 view.update(cx, |view, cx| {
8282 view.change_selections(None, cx, |s| {
8283 s.select_display_ranges([DisplayPoint::new(5, 0)..DisplayPoint::new(0, 1)])
8284 });
8285 view.split_selection_into_lines(&SplitSelectionIntoLines, cx);
8286 assert_eq!(
8287 view.display_text(cx),
8288 "aaaaa\nbbbbb\nccccc\nddddd\neeeee\nfffff\nggggg\nhhhhh\niiiii"
8289 );
8290 assert_eq!(
8291 view.selections.display_ranges(cx),
8292 [
8293 DisplayPoint::new(0, 5)..DisplayPoint::new(0, 5),
8294 DisplayPoint::new(1, 5)..DisplayPoint::new(1, 5),
8295 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
8296 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 5),
8297 DisplayPoint::new(4, 5)..DisplayPoint::new(4, 5),
8298 DisplayPoint::new(5, 5)..DisplayPoint::new(5, 5),
8299 DisplayPoint::new(6, 5)..DisplayPoint::new(6, 5),
8300 DisplayPoint::new(7, 0)..DisplayPoint::new(7, 0)
8301 ]
8302 );
8303 });
8304 }
8305
8306 #[gpui::test]
8307 fn test_add_selection_above_below(cx: &mut gpui::MutableAppContext) {
8308 cx.set_global(Settings::test(cx));
8309 let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
8310 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8311
8312 view.update(cx, |view, cx| {
8313 view.change_selections(None, cx, |s| {
8314 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)])
8315 });
8316 });
8317 view.update(cx, |view, cx| {
8318 view.add_selection_above(&AddSelectionAbove, cx);
8319 assert_eq!(
8320 view.selections.display_ranges(cx),
8321 vec![
8322 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8323 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8324 ]
8325 );
8326 });
8327
8328 view.update(cx, |view, cx| {
8329 view.add_selection_above(&AddSelectionAbove, cx);
8330 assert_eq!(
8331 view.selections.display_ranges(cx),
8332 vec![
8333 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8334 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8335 ]
8336 );
8337 });
8338
8339 view.update(cx, |view, cx| {
8340 view.add_selection_below(&AddSelectionBelow, cx);
8341 assert_eq!(
8342 view.selections.display_ranges(cx),
8343 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8344 );
8345
8346 view.undo_selection(&UndoSelection, cx);
8347 assert_eq!(
8348 view.selections.display_ranges(cx),
8349 vec![
8350 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 3),
8351 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)
8352 ]
8353 );
8354
8355 view.redo_selection(&RedoSelection, cx);
8356 assert_eq!(
8357 view.selections.display_ranges(cx),
8358 vec![DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3)]
8359 );
8360 });
8361
8362 view.update(cx, |view, cx| {
8363 view.add_selection_below(&AddSelectionBelow, cx);
8364 assert_eq!(
8365 view.selections.display_ranges(cx),
8366 vec![
8367 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8368 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8369 ]
8370 );
8371 });
8372
8373 view.update(cx, |view, cx| {
8374 view.add_selection_below(&AddSelectionBelow, cx);
8375 assert_eq!(
8376 view.selections.display_ranges(cx),
8377 vec![
8378 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 3),
8379 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 3)
8380 ]
8381 );
8382 });
8383
8384 view.update(cx, |view, cx| {
8385 view.change_selections(None, cx, |s| {
8386 s.select_display_ranges([DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)])
8387 });
8388 });
8389 view.update(cx, |view, cx| {
8390 view.add_selection_below(&AddSelectionBelow, cx);
8391 assert_eq!(
8392 view.selections.display_ranges(cx),
8393 vec![
8394 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8395 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8396 ]
8397 );
8398 });
8399
8400 view.update(cx, |view, cx| {
8401 view.add_selection_below(&AddSelectionBelow, cx);
8402 assert_eq!(
8403 view.selections.display_ranges(cx),
8404 vec![
8405 DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3),
8406 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 3)
8407 ]
8408 );
8409 });
8410
8411 view.update(cx, |view, cx| {
8412 view.add_selection_above(&AddSelectionAbove, cx);
8413 assert_eq!(
8414 view.selections.display_ranges(cx),
8415 vec![DisplayPoint::new(1, 4)..DisplayPoint::new(1, 3)]
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.change_selections(None, cx, |s| {
8429 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(1, 4)])
8430 });
8431 view.add_selection_below(&AddSelectionBelow, cx);
8432 assert_eq!(
8433 view.selections.display_ranges(cx),
8434 vec![
8435 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8436 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8437 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8438 ]
8439 );
8440 });
8441
8442 view.update(cx, |view, cx| {
8443 view.add_selection_below(&AddSelectionBelow, cx);
8444 assert_eq!(
8445 view.selections.display_ranges(cx),
8446 vec![
8447 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8448 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8449 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8450 DisplayPoint::new(4, 1)..DisplayPoint::new(4, 4),
8451 ]
8452 );
8453 });
8454
8455 view.update(cx, |view, cx| {
8456 view.add_selection_above(&AddSelectionAbove, cx);
8457 assert_eq!(
8458 view.selections.display_ranges(cx),
8459 vec![
8460 DisplayPoint::new(0, 1)..DisplayPoint::new(0, 3),
8461 DisplayPoint::new(1, 1)..DisplayPoint::new(1, 4),
8462 DisplayPoint::new(3, 1)..DisplayPoint::new(3, 2),
8463 ]
8464 );
8465 });
8466
8467 view.update(cx, |view, cx| {
8468 view.change_selections(None, cx, |s| {
8469 s.select_display_ranges([DisplayPoint::new(4, 3)..DisplayPoint::new(1, 1)])
8470 });
8471 });
8472 view.update(cx, |view, cx| {
8473 view.add_selection_above(&AddSelectionAbove, cx);
8474 assert_eq!(
8475 view.selections.display_ranges(cx),
8476 vec![
8477 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 1),
8478 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
8479 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
8480 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
8481 ]
8482 );
8483 });
8484
8485 view.update(cx, |view, cx| {
8486 view.add_selection_below(&AddSelectionBelow, cx);
8487 assert_eq!(
8488 view.selections.display_ranges(cx),
8489 vec![
8490 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 1),
8491 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 1),
8492 DisplayPoint::new(4, 3)..DisplayPoint::new(4, 1),
8493 ]
8494 );
8495 });
8496 }
8497
8498 #[gpui::test]
8499 fn test_select_next(cx: &mut gpui::MutableAppContext) {
8500 cx.set_global(Settings::test(cx));
8501
8502 let (text, ranges) = marked_text_ranges("[abc]\n[abc] [abc]\ndefabc\n[abc]");
8503 let buffer = MultiBuffer::build_simple(&text, cx);
8504 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(buffer, cx));
8505
8506 view.update(cx, |view, cx| {
8507 view.change_selections(None, cx, |s| {
8508 s.select_ranges([ranges[1].start + 1..ranges[1].start + 1])
8509 });
8510 view.select_next(
8511 &SelectNext {
8512 replace_newest: false,
8513 },
8514 cx,
8515 );
8516 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
8517
8518 view.select_next(
8519 &SelectNext {
8520 replace_newest: false,
8521 },
8522 cx,
8523 );
8524 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
8525
8526 view.undo_selection(&UndoSelection, cx);
8527 assert_eq!(view.selections.ranges(cx), &ranges[1..2]);
8528
8529 view.redo_selection(&RedoSelection, cx);
8530 assert_eq!(view.selections.ranges(cx), &ranges[1..3]);
8531
8532 view.select_next(
8533 &SelectNext {
8534 replace_newest: false,
8535 },
8536 cx,
8537 );
8538 assert_eq!(view.selections.ranges(cx), &ranges[1..4]);
8539
8540 view.select_next(
8541 &SelectNext {
8542 replace_newest: false,
8543 },
8544 cx,
8545 );
8546 assert_eq!(view.selections.ranges(cx), &ranges[0..4]);
8547 });
8548 }
8549
8550 #[gpui::test]
8551 async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
8552 cx.update(|cx| cx.set_global(Settings::test(cx)));
8553 let language = Arc::new(Language::new(
8554 LanguageConfig::default(),
8555 Some(tree_sitter_rust::language()),
8556 ));
8557
8558 let text = r#"
8559 use mod1::mod2::{mod3, mod4};
8560
8561 fn fn_1(param1: bool, param2: &str) {
8562 let var1 = "text";
8563 }
8564 "#
8565 .unindent();
8566
8567 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8568 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8569 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8570 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8571 .await;
8572
8573 view.update(cx, |view, cx| {
8574 view.change_selections(None, cx, |s| {
8575 s.select_display_ranges([
8576 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8577 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8578 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8579 ]);
8580 });
8581 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8582 });
8583 assert_eq!(
8584 view.update(cx, |view, cx| { view.selections.display_ranges(cx) }),
8585 &[
8586 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8587 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8588 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
8589 ]
8590 );
8591
8592 view.update(cx, |view, cx| {
8593 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8594 });
8595 assert_eq!(
8596 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8597 &[
8598 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8599 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8600 ]
8601 );
8602
8603 view.update(cx, |view, cx| {
8604 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8605 });
8606 assert_eq!(
8607 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8608 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8609 );
8610
8611 // Trying to expand the selected syntax node one more time has no effect.
8612 view.update(cx, |view, cx| {
8613 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8614 });
8615 assert_eq!(
8616 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8617 &[DisplayPoint::new(5, 0)..DisplayPoint::new(0, 0)]
8618 );
8619
8620 view.update(cx, |view, cx| {
8621 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8622 });
8623 assert_eq!(
8624 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8625 &[
8626 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8627 DisplayPoint::new(4, 1)..DisplayPoint::new(2, 0),
8628 ]
8629 );
8630
8631 view.update(cx, |view, cx| {
8632 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8633 });
8634 assert_eq!(
8635 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8636 &[
8637 DisplayPoint::new(0, 23)..DisplayPoint::new(0, 27),
8638 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8639 DisplayPoint::new(3, 15)..DisplayPoint::new(3, 21),
8640 ]
8641 );
8642
8643 view.update(cx, |view, cx| {
8644 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8645 });
8646 assert_eq!(
8647 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8648 &[
8649 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8650 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8651 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8652 ]
8653 );
8654
8655 // Trying to shrink the selected syntax node one more time has no effect.
8656 view.update(cx, |view, cx| {
8657 view.select_smaller_syntax_node(&SelectSmallerSyntaxNode, cx);
8658 });
8659 assert_eq!(
8660 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8661 &[
8662 DisplayPoint::new(0, 25)..DisplayPoint::new(0, 25),
8663 DisplayPoint::new(2, 24)..DisplayPoint::new(2, 12),
8664 DisplayPoint::new(3, 18)..DisplayPoint::new(3, 18),
8665 ]
8666 );
8667
8668 // Ensure that we keep expanding the selection if the larger selection starts or ends within
8669 // a fold.
8670 view.update(cx, |view, cx| {
8671 view.fold_ranges(
8672 vec![
8673 Point::new(0, 21)..Point::new(0, 24),
8674 Point::new(3, 20)..Point::new(3, 22),
8675 ],
8676 cx,
8677 );
8678 view.select_larger_syntax_node(&SelectLargerSyntaxNode, cx);
8679 });
8680 assert_eq!(
8681 view.update(cx, |view, cx| view.selections.display_ranges(cx)),
8682 &[
8683 DisplayPoint::new(0, 16)..DisplayPoint::new(0, 28),
8684 DisplayPoint::new(2, 35)..DisplayPoint::new(2, 7),
8685 DisplayPoint::new(3, 4)..DisplayPoint::new(3, 23),
8686 ]
8687 );
8688 }
8689
8690 #[gpui::test]
8691 async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
8692 cx.update(|cx| cx.set_global(Settings::test(cx)));
8693 let language = Arc::new(
8694 Language::new(
8695 LanguageConfig {
8696 brackets: vec![
8697 BracketPair {
8698 start: "{".to_string(),
8699 end: "}".to_string(),
8700 close: false,
8701 newline: true,
8702 },
8703 BracketPair {
8704 start: "(".to_string(),
8705 end: ")".to_string(),
8706 close: false,
8707 newline: true,
8708 },
8709 ],
8710 ..Default::default()
8711 },
8712 Some(tree_sitter_rust::language()),
8713 )
8714 .with_indents_query(
8715 r#"
8716 (_ "(" ")" @end) @indent
8717 (_ "{" "}" @end) @indent
8718 "#,
8719 )
8720 .unwrap(),
8721 );
8722
8723 let text = "fn a() {}";
8724
8725 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8726 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8727 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
8728 editor
8729 .condition(&cx, |editor, cx| !editor.buffer.read(cx).is_parsing(cx))
8730 .await;
8731
8732 editor.update(cx, |editor, cx| {
8733 editor.change_selections(None, cx, |s| s.select_ranges([5..5, 8..8, 9..9]));
8734 editor.newline(&Newline, cx);
8735 assert_eq!(editor.text(cx), "fn a(\n \n) {\n \n}\n");
8736 assert_eq!(
8737 editor.selections.ranges(cx),
8738 &[
8739 Point::new(1, 4)..Point::new(1, 4),
8740 Point::new(3, 4)..Point::new(3, 4),
8741 Point::new(5, 0)..Point::new(5, 0)
8742 ]
8743 );
8744 });
8745 }
8746
8747 #[gpui::test]
8748 async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
8749 cx.update(|cx| cx.set_global(Settings::test(cx)));
8750 let language = Arc::new(Language::new(
8751 LanguageConfig {
8752 brackets: vec![
8753 BracketPair {
8754 start: "{".to_string(),
8755 end: "}".to_string(),
8756 close: true,
8757 newline: true,
8758 },
8759 BracketPair {
8760 start: "/*".to_string(),
8761 end: " */".to_string(),
8762 close: true,
8763 newline: true,
8764 },
8765 BracketPair {
8766 start: "[".to_string(),
8767 end: "]".to_string(),
8768 close: false,
8769 newline: true,
8770 },
8771 ],
8772 autoclose_before: "})]".to_string(),
8773 ..Default::default()
8774 },
8775 Some(tree_sitter_rust::language()),
8776 ));
8777
8778 let text = r#"
8779 a
8780
8781 /
8782
8783 "#
8784 .unindent();
8785
8786 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8787 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8788 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8789 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8790 .await;
8791
8792 view.update(cx, |view, cx| {
8793 view.change_selections(None, cx, |s| {
8794 s.select_display_ranges([
8795 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8796 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 0),
8797 ])
8798 });
8799
8800 view.handle_input(&Input("{".to_string()), cx);
8801 view.handle_input(&Input("{".to_string()), cx);
8802 view.handle_input(&Input("{".to_string()), cx);
8803 assert_eq!(
8804 view.text(cx),
8805 "
8806 {{{}}}
8807 {{{}}}
8808 /
8809
8810 "
8811 .unindent()
8812 );
8813
8814 view.move_right(&MoveRight, cx);
8815 view.handle_input(&Input("}".to_string()), cx);
8816 view.handle_input(&Input("}".to_string()), cx);
8817 view.handle_input(&Input("}".to_string()), cx);
8818 assert_eq!(
8819 view.text(cx),
8820 "
8821 {{{}}}}
8822 {{{}}}}
8823 /
8824
8825 "
8826 .unindent()
8827 );
8828
8829 view.undo(&Undo, cx);
8830 view.handle_input(&Input("/".to_string()), cx);
8831 view.handle_input(&Input("*".to_string()), cx);
8832 assert_eq!(
8833 view.text(cx),
8834 "
8835 /* */
8836 /* */
8837 /
8838
8839 "
8840 .unindent()
8841 );
8842
8843 view.undo(&Undo, cx);
8844 view.change_selections(None, cx, |s| {
8845 s.select_display_ranges([
8846 DisplayPoint::new(2, 1)..DisplayPoint::new(2, 1),
8847 DisplayPoint::new(3, 0)..DisplayPoint::new(3, 0),
8848 ])
8849 });
8850 view.handle_input(&Input("*".to_string()), cx);
8851 assert_eq!(
8852 view.text(cx),
8853 "
8854 a
8855
8856 /*
8857 *
8858 "
8859 .unindent()
8860 );
8861
8862 // Don't autoclose if the next character isn't whitespace and isn't
8863 // listed in the language's "autoclose_before" section.
8864 view.finalize_last_transaction(cx);
8865 view.change_selections(None, cx, |s| {
8866 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 0)])
8867 });
8868 view.handle_input(&Input("{".to_string()), cx);
8869 assert_eq!(
8870 view.text(cx),
8871 "
8872 {a
8873
8874 /*
8875 *
8876 "
8877 .unindent()
8878 );
8879
8880 view.undo(&Undo, cx);
8881 view.change_selections(None, cx, |s| {
8882 s.select_display_ranges([DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1)])
8883 });
8884 view.handle_input(&Input("{".to_string()), cx);
8885 assert_eq!(
8886 view.text(cx),
8887 "
8888 {a}
8889
8890 /*
8891 *
8892 "
8893 .unindent()
8894 );
8895 assert_eq!(
8896 view.selections.display_ranges(cx),
8897 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
8898 );
8899
8900 view.undo(&Undo, cx);
8901 view.handle_input(&Input("[".to_string()), cx);
8902 assert_eq!(
8903 view.text(cx),
8904 "
8905 [a]
8906
8907 /*
8908 *
8909 "
8910 .unindent()
8911 );
8912 assert_eq!(
8913 view.selections.display_ranges(cx),
8914 [DisplayPoint::new(0, 1)..DisplayPoint::new(0, 2)]
8915 );
8916
8917 view.undo(&Undo, cx);
8918 view.change_selections(None, cx, |s| {
8919 s.select_display_ranges([DisplayPoint::new(0, 1)..DisplayPoint::new(0, 1)])
8920 });
8921 view.handle_input(&Input("[".to_string()), cx);
8922 assert_eq!(
8923 view.text(cx),
8924 "
8925 a[
8926
8927 /*
8928 *
8929 "
8930 .unindent()
8931 );
8932 assert_eq!(
8933 view.selections.display_ranges(cx),
8934 [DisplayPoint::new(0, 2)..DisplayPoint::new(0, 2)]
8935 );
8936 });
8937 }
8938
8939 #[gpui::test]
8940 async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
8941 cx.update(|cx| cx.set_global(Settings::test(cx)));
8942 let language = Arc::new(Language::new(
8943 LanguageConfig {
8944 brackets: vec![BracketPair {
8945 start: "{".to_string(),
8946 end: "}".to_string(),
8947 close: true,
8948 newline: true,
8949 }],
8950 ..Default::default()
8951 },
8952 Some(tree_sitter_rust::language()),
8953 ));
8954
8955 let text = r#"
8956 a
8957 b
8958 c
8959 "#
8960 .unindent();
8961
8962 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
8963 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
8964 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
8965 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
8966 .await;
8967
8968 view.update(cx, |view, cx| {
8969 view.change_selections(None, cx, |s| {
8970 s.select_display_ranges([
8971 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
8972 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
8973 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1),
8974 ])
8975 });
8976
8977 view.handle_input(&Input("{".to_string()), cx);
8978 view.handle_input(&Input("{".to_string()), cx);
8979 view.handle_input(&Input("{".to_string()), cx);
8980 assert_eq!(
8981 view.text(cx),
8982 "
8983 {{{a}}}
8984 {{{b}}}
8985 {{{c}}}
8986 "
8987 .unindent()
8988 );
8989 assert_eq!(
8990 view.selections.display_ranges(cx),
8991 [
8992 DisplayPoint::new(0, 3)..DisplayPoint::new(0, 4),
8993 DisplayPoint::new(1, 3)..DisplayPoint::new(1, 4),
8994 DisplayPoint::new(2, 3)..DisplayPoint::new(2, 4)
8995 ]
8996 );
8997
8998 view.undo(&Undo, cx);
8999 assert_eq!(
9000 view.text(cx),
9001 "
9002 a
9003 b
9004 c
9005 "
9006 .unindent()
9007 );
9008 assert_eq!(
9009 view.selections.display_ranges(cx),
9010 [
9011 DisplayPoint::new(0, 0)..DisplayPoint::new(0, 1),
9012 DisplayPoint::new(1, 0)..DisplayPoint::new(1, 1),
9013 DisplayPoint::new(2, 0)..DisplayPoint::new(2, 1)
9014 ]
9015 );
9016 });
9017 }
9018
9019 #[gpui::test]
9020 async fn test_snippets(cx: &mut gpui::TestAppContext) {
9021 cx.update(|cx| cx.set_global(Settings::test(cx)));
9022
9023 let (text, insertion_ranges) = marked_text_ranges(indoc! {"
9024 a.| b
9025 a.| b
9026 a.| b"});
9027 let buffer = cx.update(|cx| MultiBuffer::build_simple(&text, cx));
9028 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9029
9030 editor.update(cx, |editor, cx| {
9031 let snippet = Snippet::parse("f(${1:one}, ${2:two}, ${1:three})$0").unwrap();
9032
9033 editor
9034 .insert_snippet(&insertion_ranges, snippet, cx)
9035 .unwrap();
9036
9037 fn assert(editor: &mut Editor, cx: &mut ViewContext<Editor>, marked_text_ranges: &str) {
9038 let range_markers = ('<', '>');
9039 let (expected_text, mut selection_ranges_lookup) =
9040 marked_text_ranges_by(marked_text_ranges, vec![range_markers.clone().into()]);
9041 let selection_ranges = selection_ranges_lookup
9042 .remove(&range_markers.into())
9043 .unwrap();
9044 assert_eq!(editor.text(cx), expected_text);
9045 assert_eq!(editor.selections.ranges::<usize>(cx), selection_ranges);
9046 }
9047 assert(
9048 editor,
9049 cx,
9050 indoc! {"
9051 a.f(<one>, two, <three>) b
9052 a.f(<one>, two, <three>) b
9053 a.f(<one>, two, <three>) b"},
9054 );
9055
9056 // Can't move earlier than the first tab stop
9057 assert!(!editor.move_to_prev_snippet_tabstop(cx));
9058 assert(
9059 editor,
9060 cx,
9061 indoc! {"
9062 a.f(<one>, two, <three>) b
9063 a.f(<one>, two, <three>) b
9064 a.f(<one>, two, <three>) b"},
9065 );
9066
9067 assert!(editor.move_to_next_snippet_tabstop(cx));
9068 assert(
9069 editor,
9070 cx,
9071 indoc! {"
9072 a.f(one, <two>, three) b
9073 a.f(one, <two>, three) b
9074 a.f(one, <two>, three) b"},
9075 );
9076
9077 editor.move_to_prev_snippet_tabstop(cx);
9078 assert(
9079 editor,
9080 cx,
9081 indoc! {"
9082 a.f(<one>, two, <three>) b
9083 a.f(<one>, two, <three>) b
9084 a.f(<one>, two, <three>) b"},
9085 );
9086
9087 assert!(editor.move_to_next_snippet_tabstop(cx));
9088 assert(
9089 editor,
9090 cx,
9091 indoc! {"
9092 a.f(one, <two>, three) b
9093 a.f(one, <two>, three) b
9094 a.f(one, <two>, three) b"},
9095 );
9096 assert!(editor.move_to_next_snippet_tabstop(cx));
9097 assert(
9098 editor,
9099 cx,
9100 indoc! {"
9101 a.f(one, two, three)<> b
9102 a.f(one, two, three)<> b
9103 a.f(one, two, three)<> b"},
9104 );
9105
9106 // As soon as the last tab stop is reached, snippet state is gone
9107 editor.move_to_prev_snippet_tabstop(cx);
9108 assert(
9109 editor,
9110 cx,
9111 indoc! {"
9112 a.f(one, two, three)<> b
9113 a.f(one, two, three)<> b
9114 a.f(one, two, three)<> b"},
9115 );
9116 });
9117 }
9118
9119 #[gpui::test]
9120 async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
9121 cx.foreground().forbid_parking();
9122
9123 let mut language = Language::new(
9124 LanguageConfig {
9125 name: "Rust".into(),
9126 path_suffixes: vec!["rs".to_string()],
9127 ..Default::default()
9128 },
9129 Some(tree_sitter_rust::language()),
9130 );
9131 let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
9132 capabilities: lsp::ServerCapabilities {
9133 document_formatting_provider: Some(lsp::OneOf::Left(true)),
9134 ..Default::default()
9135 },
9136 ..Default::default()
9137 });
9138
9139 let fs = FakeFs::new(cx.background().clone());
9140 fs.insert_file("/file.rs", Default::default()).await;
9141
9142 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9143 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9144 let buffer = project
9145 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9146 .await
9147 .unwrap();
9148
9149 cx.foreground().start_waiting();
9150 let fake_server = fake_servers.next().await.unwrap();
9151
9152 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9153 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9154 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9155 assert!(cx.read(|cx| editor.is_dirty(cx)));
9156
9157 let save = cx.update(|cx| editor.save(project.clone(), cx));
9158 fake_server
9159 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9160 assert_eq!(
9161 params.text_document.uri,
9162 lsp::Url::from_file_path("/file.rs").unwrap()
9163 );
9164 assert_eq!(params.options.tab_size, 4);
9165 Ok(Some(vec![lsp::TextEdit::new(
9166 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
9167 ", ".to_string(),
9168 )]))
9169 })
9170 .next()
9171 .await;
9172 cx.foreground().start_waiting();
9173 save.await.unwrap();
9174 assert_eq!(
9175 editor.read_with(cx, |editor, cx| editor.text(cx)),
9176 "one, two\nthree\n"
9177 );
9178 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9179
9180 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9181 assert!(cx.read(|cx| editor.is_dirty(cx)));
9182
9183 // Ensure we can still save even if formatting hangs.
9184 fake_server.handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9185 assert_eq!(
9186 params.text_document.uri,
9187 lsp::Url::from_file_path("/file.rs").unwrap()
9188 );
9189 futures::future::pending::<()>().await;
9190 unreachable!()
9191 });
9192 let save = cx.update(|cx| editor.save(project.clone(), cx));
9193 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
9194 cx.foreground().start_waiting();
9195 save.await.unwrap();
9196 assert_eq!(
9197 editor.read_with(cx, |editor, cx| editor.text(cx)),
9198 "one\ntwo\nthree\n"
9199 );
9200 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9201
9202 // Set rust language override and assert overriden tabsize is sent to language server
9203 cx.update(|cx| {
9204 cx.update_global::<Settings, _, _>(|settings, _| {
9205 settings.language_overrides.insert(
9206 "Rust".into(),
9207 LanguageSettings {
9208 tab_size: Some(8.try_into().unwrap()),
9209 ..Default::default()
9210 },
9211 );
9212 })
9213 });
9214
9215 let save = cx.update(|cx| editor.save(project.clone(), cx));
9216 fake_server
9217 .handle_request::<lsp::request::Formatting, _, _>(move |params, _| async move {
9218 assert_eq!(
9219 params.text_document.uri,
9220 lsp::Url::from_file_path("/file.rs").unwrap()
9221 );
9222 assert_eq!(params.options.tab_size, 8);
9223 Ok(Some(vec![]))
9224 })
9225 .next()
9226 .await;
9227 cx.foreground().start_waiting();
9228 save.await.unwrap();
9229 }
9230
9231 #[gpui::test]
9232 async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
9233 cx.foreground().forbid_parking();
9234
9235 let mut language = Language::new(
9236 LanguageConfig {
9237 name: "Rust".into(),
9238 path_suffixes: vec!["rs".to_string()],
9239 ..Default::default()
9240 },
9241 Some(tree_sitter_rust::language()),
9242 );
9243 let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
9244 capabilities: lsp::ServerCapabilities {
9245 document_range_formatting_provider: Some(lsp::OneOf::Left(true)),
9246 ..Default::default()
9247 },
9248 ..Default::default()
9249 });
9250
9251 let fs = FakeFs::new(cx.background().clone());
9252 fs.insert_file("/file.rs", Default::default()).await;
9253
9254 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9255 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9256 let buffer = project
9257 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9258 .await
9259 .unwrap();
9260
9261 cx.foreground().start_waiting();
9262 let fake_server = fake_servers.next().await.unwrap();
9263
9264 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9265 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9266 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9267 assert!(cx.read(|cx| editor.is_dirty(cx)));
9268
9269 let save = cx.update(|cx| editor.save(project.clone(), cx));
9270 fake_server
9271 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
9272 assert_eq!(
9273 params.text_document.uri,
9274 lsp::Url::from_file_path("/file.rs").unwrap()
9275 );
9276 assert_eq!(params.options.tab_size, 4);
9277 Ok(Some(vec![lsp::TextEdit::new(
9278 lsp::Range::new(lsp::Position::new(0, 3), lsp::Position::new(1, 0)),
9279 ", ".to_string(),
9280 )]))
9281 })
9282 .next()
9283 .await;
9284 cx.foreground().start_waiting();
9285 save.await.unwrap();
9286 assert_eq!(
9287 editor.read_with(cx, |editor, cx| editor.text(cx)),
9288 "one, two\nthree\n"
9289 );
9290 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9291
9292 editor.update(cx, |editor, cx| editor.set_text("one\ntwo\nthree\n", cx));
9293 assert!(cx.read(|cx| editor.is_dirty(cx)));
9294
9295 // Ensure we can still save even if formatting hangs.
9296 fake_server.handle_request::<lsp::request::RangeFormatting, _, _>(
9297 move |params, _| async move {
9298 assert_eq!(
9299 params.text_document.uri,
9300 lsp::Url::from_file_path("/file.rs").unwrap()
9301 );
9302 futures::future::pending::<()>().await;
9303 unreachable!()
9304 },
9305 );
9306 let save = cx.update(|cx| editor.save(project.clone(), cx));
9307 cx.foreground().advance_clock(items::FORMAT_TIMEOUT);
9308 cx.foreground().start_waiting();
9309 save.await.unwrap();
9310 assert_eq!(
9311 editor.read_with(cx, |editor, cx| editor.text(cx)),
9312 "one\ntwo\nthree\n"
9313 );
9314 assert!(!cx.read(|cx| editor.is_dirty(cx)));
9315
9316 // Set rust language override and assert overriden tabsize is sent to language server
9317 cx.update(|cx| {
9318 cx.update_global::<Settings, _, _>(|settings, _| {
9319 settings.language_overrides.insert(
9320 "Rust".into(),
9321 LanguageSettings {
9322 tab_size: Some(8.try_into().unwrap()),
9323 ..Default::default()
9324 },
9325 );
9326 })
9327 });
9328
9329 let save = cx.update(|cx| editor.save(project.clone(), cx));
9330 fake_server
9331 .handle_request::<lsp::request::RangeFormatting, _, _>(move |params, _| async move {
9332 assert_eq!(
9333 params.text_document.uri,
9334 lsp::Url::from_file_path("/file.rs").unwrap()
9335 );
9336 assert_eq!(params.options.tab_size, 8);
9337 Ok(Some(vec![]))
9338 })
9339 .next()
9340 .await;
9341 cx.foreground().start_waiting();
9342 save.await.unwrap();
9343 }
9344
9345 #[gpui::test]
9346 async fn test_completion(cx: &mut gpui::TestAppContext) {
9347 let mut language = Language::new(
9348 LanguageConfig {
9349 name: "Rust".into(),
9350 path_suffixes: vec!["rs".to_string()],
9351 ..Default::default()
9352 },
9353 Some(tree_sitter_rust::language()),
9354 );
9355 let mut fake_servers = language.set_fake_lsp_adapter(FakeLspAdapter {
9356 capabilities: lsp::ServerCapabilities {
9357 completion_provider: Some(lsp::CompletionOptions {
9358 trigger_characters: Some(vec![".".to_string(), ":".to_string()]),
9359 ..Default::default()
9360 }),
9361 ..Default::default()
9362 },
9363 ..Default::default()
9364 });
9365
9366 let text = "
9367 one
9368 two
9369 three
9370 "
9371 .unindent();
9372
9373 let fs = FakeFs::new(cx.background().clone());
9374 fs.insert_file("/file.rs", text).await;
9375
9376 let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
9377 project.update(cx, |project, _| project.languages().add(Arc::new(language)));
9378 let buffer = project
9379 .update(cx, |project, cx| project.open_local_buffer("/file.rs", cx))
9380 .await
9381 .unwrap();
9382 let mut fake_server = fake_servers.next().await.unwrap();
9383
9384 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9385 let (_, editor) = cx.add_window(|cx| build_editor(buffer, cx));
9386
9387 editor.update(cx, |editor, cx| {
9388 editor.project = Some(project);
9389 editor.change_selections(None, cx, |s| {
9390 s.select_ranges([Point::new(0, 3)..Point::new(0, 3)])
9391 });
9392 editor.handle_input(&Input(".".to_string()), cx);
9393 });
9394
9395 handle_completion_request(
9396 &mut fake_server,
9397 "/file.rs",
9398 Point::new(0, 4),
9399 vec![
9400 (Point::new(0, 4)..Point::new(0, 4), "first_completion"),
9401 (Point::new(0, 4)..Point::new(0, 4), "second_completion"),
9402 ],
9403 )
9404 .await;
9405 editor
9406 .condition(&cx, |editor, _| editor.context_menu_visible())
9407 .await;
9408
9409 let apply_additional_edits = editor.update(cx, |editor, cx| {
9410 editor.move_down(&MoveDown, cx);
9411 let apply_additional_edits = editor
9412 .confirm_completion(&ConfirmCompletion::default(), cx)
9413 .unwrap();
9414 assert_eq!(
9415 editor.text(cx),
9416 "
9417 one.second_completion
9418 two
9419 three
9420 "
9421 .unindent()
9422 );
9423 apply_additional_edits
9424 });
9425
9426 handle_resolve_completion_request(
9427 &mut fake_server,
9428 Some((Point::new(2, 5)..Point::new(2, 5), "\nadditional edit")),
9429 )
9430 .await;
9431 apply_additional_edits.await.unwrap();
9432 assert_eq!(
9433 editor.read_with(cx, |editor, cx| editor.text(cx)),
9434 "
9435 one.second_completion
9436 two
9437 three
9438 additional edit
9439 "
9440 .unindent()
9441 );
9442
9443 editor.update(cx, |editor, cx| {
9444 editor.change_selections(None, cx, |s| {
9445 s.select_ranges([
9446 Point::new(1, 3)..Point::new(1, 3),
9447 Point::new(2, 5)..Point::new(2, 5),
9448 ])
9449 });
9450
9451 editor.handle_input(&Input(" ".to_string()), cx);
9452 assert!(editor.context_menu.is_none());
9453 editor.handle_input(&Input("s".to_string()), cx);
9454 assert!(editor.context_menu.is_none());
9455 });
9456
9457 handle_completion_request(
9458 &mut fake_server,
9459 "/file.rs",
9460 Point::new(2, 7),
9461 vec![
9462 (Point::new(2, 6)..Point::new(2, 7), "fourth_completion"),
9463 (Point::new(2, 6)..Point::new(2, 7), "fifth_completion"),
9464 (Point::new(2, 6)..Point::new(2, 7), "sixth_completion"),
9465 ],
9466 )
9467 .await;
9468 editor
9469 .condition(&cx, |editor, _| editor.context_menu_visible())
9470 .await;
9471
9472 editor.update(cx, |editor, cx| {
9473 editor.handle_input(&Input("i".to_string()), cx);
9474 });
9475
9476 handle_completion_request(
9477 &mut fake_server,
9478 "/file.rs",
9479 Point::new(2, 8),
9480 vec![
9481 (Point::new(2, 6)..Point::new(2, 8), "fourth_completion"),
9482 (Point::new(2, 6)..Point::new(2, 8), "fifth_completion"),
9483 (Point::new(2, 6)..Point::new(2, 8), "sixth_completion"),
9484 ],
9485 )
9486 .await;
9487 editor
9488 .condition(&cx, |editor, _| editor.context_menu_visible())
9489 .await;
9490
9491 let apply_additional_edits = editor.update(cx, |editor, cx| {
9492 let apply_additional_edits = editor
9493 .confirm_completion(&ConfirmCompletion::default(), cx)
9494 .unwrap();
9495 assert_eq!(
9496 editor.text(cx),
9497 "
9498 one.second_completion
9499 two sixth_completion
9500 three sixth_completion
9501 additional edit
9502 "
9503 .unindent()
9504 );
9505 apply_additional_edits
9506 });
9507 handle_resolve_completion_request(&mut fake_server, None).await;
9508 apply_additional_edits.await.unwrap();
9509
9510 async fn handle_completion_request(
9511 fake: &mut FakeLanguageServer,
9512 path: &'static str,
9513 position: Point,
9514 completions: Vec<(Range<Point>, &'static str)>,
9515 ) {
9516 fake.handle_request::<lsp::request::Completion, _, _>(move |params, _| {
9517 let completions = completions.clone();
9518 async move {
9519 assert_eq!(
9520 params.text_document_position.text_document.uri,
9521 lsp::Url::from_file_path(path).unwrap()
9522 );
9523 assert_eq!(
9524 params.text_document_position.position,
9525 lsp::Position::new(position.row, position.column)
9526 );
9527 Ok(Some(lsp::CompletionResponse::Array(
9528 completions
9529 .iter()
9530 .map(|(range, new_text)| lsp::CompletionItem {
9531 label: new_text.to_string(),
9532 text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
9533 range: lsp::Range::new(
9534 lsp::Position::new(range.start.row, range.start.column),
9535 lsp::Position::new(range.start.row, range.start.column),
9536 ),
9537 new_text: new_text.to_string(),
9538 })),
9539 ..Default::default()
9540 })
9541 .collect(),
9542 )))
9543 }
9544 })
9545 .next()
9546 .await;
9547 }
9548
9549 async fn handle_resolve_completion_request(
9550 fake: &mut FakeLanguageServer,
9551 edit: Option<(Range<Point>, &'static str)>,
9552 ) {
9553 fake.handle_request::<lsp::request::ResolveCompletionItem, _, _>(move |_, _| {
9554 let edit = edit.clone();
9555 async move {
9556 Ok(lsp::CompletionItem {
9557 additional_text_edits: edit.map(|(range, new_text)| {
9558 vec![lsp::TextEdit::new(
9559 lsp::Range::new(
9560 lsp::Position::new(range.start.row, range.start.column),
9561 lsp::Position::new(range.end.row, range.end.column),
9562 ),
9563 new_text.to_string(),
9564 )]
9565 }),
9566 ..Default::default()
9567 })
9568 }
9569 })
9570 .next()
9571 .await;
9572 }
9573 }
9574
9575 #[gpui::test]
9576 async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
9577 cx.update(|cx| cx.set_global(Settings::test(cx)));
9578 let language = Arc::new(Language::new(
9579 LanguageConfig {
9580 line_comment: Some("// ".to_string()),
9581 ..Default::default()
9582 },
9583 Some(tree_sitter_rust::language()),
9584 ));
9585
9586 let text = "
9587 fn a() {
9588 //b();
9589 // c();
9590 // d();
9591 }
9592 "
9593 .unindent();
9594
9595 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9596 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9597 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9598
9599 view.update(cx, |editor, cx| {
9600 // If multiple selections intersect a line, the line is only
9601 // toggled once.
9602 editor.change_selections(None, cx, |s| {
9603 s.select_display_ranges([
9604 DisplayPoint::new(1, 3)..DisplayPoint::new(2, 3),
9605 DisplayPoint::new(3, 5)..DisplayPoint::new(3, 6),
9606 ])
9607 });
9608 editor.toggle_comments(&ToggleComments, cx);
9609 assert_eq!(
9610 editor.text(cx),
9611 "
9612 fn a() {
9613 b();
9614 c();
9615 d();
9616 }
9617 "
9618 .unindent()
9619 );
9620
9621 // The comment prefix is inserted at the same column for every line
9622 // in a selection.
9623 editor.change_selections(None, cx, |s| {
9624 s.select_display_ranges([DisplayPoint::new(1, 3)..DisplayPoint::new(3, 6)])
9625 });
9626 editor.toggle_comments(&ToggleComments, cx);
9627 assert_eq!(
9628 editor.text(cx),
9629 "
9630 fn a() {
9631 // b();
9632 // c();
9633 // d();
9634 }
9635 "
9636 .unindent()
9637 );
9638
9639 // If a selection ends at the beginning of a line, that line is not toggled.
9640 editor.change_selections(None, cx, |s| {
9641 s.select_display_ranges([DisplayPoint::new(2, 0)..DisplayPoint::new(3, 0)])
9642 });
9643 editor.toggle_comments(&ToggleComments, cx);
9644 assert_eq!(
9645 editor.text(cx),
9646 "
9647 fn a() {
9648 // b();
9649 c();
9650 // d();
9651 }
9652 "
9653 .unindent()
9654 );
9655 });
9656 }
9657
9658 #[gpui::test]
9659 fn test_editing_disjoint_excerpts(cx: &mut gpui::MutableAppContext) {
9660 cx.set_global(Settings::test(cx));
9661 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9662 let multibuffer = cx.add_model(|cx| {
9663 let mut multibuffer = MultiBuffer::new(0);
9664 multibuffer.push_excerpts(
9665 buffer.clone(),
9666 [
9667 ExcerptRange {
9668 context: Point::new(0, 0)..Point::new(0, 4),
9669 primary: None,
9670 },
9671 ExcerptRange {
9672 context: Point::new(1, 0)..Point::new(1, 4),
9673 primary: None,
9674 },
9675 ],
9676 cx,
9677 );
9678 multibuffer
9679 });
9680
9681 assert_eq!(multibuffer.read(cx).read(cx).text(), "aaaa\nbbbb");
9682
9683 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
9684 view.update(cx, |view, cx| {
9685 assert_eq!(view.text(cx), "aaaa\nbbbb");
9686 view.change_selections(None, cx, |s| {
9687 s.select_ranges([
9688 Point::new(0, 0)..Point::new(0, 0),
9689 Point::new(1, 0)..Point::new(1, 0),
9690 ])
9691 });
9692
9693 view.handle_input(&Input("X".to_string()), cx);
9694 assert_eq!(view.text(cx), "Xaaaa\nXbbbb");
9695 assert_eq!(
9696 view.selections.ranges(cx),
9697 [
9698 Point::new(0, 1)..Point::new(0, 1),
9699 Point::new(1, 1)..Point::new(1, 1),
9700 ]
9701 )
9702 });
9703 }
9704
9705 #[gpui::test]
9706 fn test_editing_overlapping_excerpts(cx: &mut gpui::MutableAppContext) {
9707 cx.set_global(Settings::test(cx));
9708 let (initial_text, excerpt_ranges) = marked_text_ranges(indoc! {"
9709 [aaaa
9710 (bbbb]
9711 cccc)"});
9712 let excerpt_ranges = excerpt_ranges.into_iter().map(|context| ExcerptRange {
9713 context,
9714 primary: None,
9715 });
9716 let buffer = cx.add_model(|cx| Buffer::new(0, initial_text, cx));
9717 let multibuffer = cx.add_model(|cx| {
9718 let mut multibuffer = MultiBuffer::new(0);
9719 multibuffer.push_excerpts(buffer, excerpt_ranges, cx);
9720 multibuffer
9721 });
9722
9723 let (_, view) = cx.add_window(Default::default(), |cx| build_editor(multibuffer, cx));
9724 view.update(cx, |view, cx| {
9725 let (expected_text, selection_ranges) = marked_text_ranges(indoc! {"
9726 aaaa
9727 b|bbb
9728 b|bb|b
9729 cccc"});
9730 assert_eq!(view.text(cx), expected_text);
9731 view.change_selections(None, cx, |s| s.select_ranges(selection_ranges));
9732
9733 view.handle_input(&Input("X".to_string()), cx);
9734
9735 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
9736 aaaa
9737 bX|bbXb
9738 bX|bbX|b
9739 cccc"});
9740 assert_eq!(view.text(cx), expected_text);
9741 assert_eq!(view.selections.ranges(cx), expected_selections);
9742
9743 view.newline(&Newline, cx);
9744 let (expected_text, expected_selections) = marked_text_ranges(indoc! {"
9745 aaaa
9746 bX
9747 |bbX
9748 b
9749 bX
9750 |bbX
9751 |b
9752 cccc"});
9753 assert_eq!(view.text(cx), expected_text);
9754 assert_eq!(view.selections.ranges(cx), expected_selections);
9755 });
9756 }
9757
9758 #[gpui::test]
9759 fn test_refresh_selections(cx: &mut gpui::MutableAppContext) {
9760 cx.set_global(Settings::test(cx));
9761 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9762 let mut excerpt1_id = None;
9763 let multibuffer = cx.add_model(|cx| {
9764 let mut multibuffer = MultiBuffer::new(0);
9765 excerpt1_id = multibuffer
9766 .push_excerpts(
9767 buffer.clone(),
9768 [
9769 ExcerptRange {
9770 context: Point::new(0, 0)..Point::new(1, 4),
9771 primary: None,
9772 },
9773 ExcerptRange {
9774 context: Point::new(1, 0)..Point::new(2, 4),
9775 primary: None,
9776 },
9777 ],
9778 cx,
9779 )
9780 .into_iter()
9781 .next();
9782 multibuffer
9783 });
9784 assert_eq!(
9785 multibuffer.read(cx).read(cx).text(),
9786 "aaaa\nbbbb\nbbbb\ncccc"
9787 );
9788 let (_, editor) = cx.add_window(Default::default(), |cx| {
9789 let mut editor = build_editor(multibuffer.clone(), cx);
9790 let snapshot = editor.snapshot(cx);
9791 editor.change_selections(None, cx, |s| {
9792 s.select_ranges([Point::new(1, 3)..Point::new(1, 3)])
9793 });
9794 editor.begin_selection(Point::new(2, 1).to_display_point(&snapshot), true, 1, cx);
9795 assert_eq!(
9796 editor.selections.ranges(cx),
9797 [
9798 Point::new(1, 3)..Point::new(1, 3),
9799 Point::new(2, 1)..Point::new(2, 1),
9800 ]
9801 );
9802 editor
9803 });
9804
9805 // Refreshing selections is a no-op when excerpts haven't changed.
9806 editor.update(cx, |editor, cx| {
9807 editor.change_selections(None, cx, |s| {
9808 s.refresh();
9809 });
9810 assert_eq!(
9811 editor.selections.ranges(cx),
9812 [
9813 Point::new(1, 3)..Point::new(1, 3),
9814 Point::new(2, 1)..Point::new(2, 1),
9815 ]
9816 );
9817 });
9818
9819 multibuffer.update(cx, |multibuffer, cx| {
9820 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
9821 });
9822 editor.update(cx, |editor, cx| {
9823 // Removing an excerpt causes the first selection to become degenerate.
9824 assert_eq!(
9825 editor.selections.ranges(cx),
9826 [
9827 Point::new(0, 0)..Point::new(0, 0),
9828 Point::new(0, 1)..Point::new(0, 1)
9829 ]
9830 );
9831
9832 // Refreshing selections will relocate the first selection to the original buffer
9833 // location.
9834 editor.change_selections(None, cx, |s| {
9835 s.refresh();
9836 });
9837 assert_eq!(
9838 editor.selections.ranges(cx),
9839 [
9840 Point::new(0, 1)..Point::new(0, 1),
9841 Point::new(0, 3)..Point::new(0, 3)
9842 ]
9843 );
9844 assert!(editor.selections.pending_anchor().is_some());
9845 });
9846 }
9847
9848 #[gpui::test]
9849 fn test_refresh_selections_while_selecting_with_mouse(cx: &mut gpui::MutableAppContext) {
9850 cx.set_global(Settings::test(cx));
9851 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
9852 let mut excerpt1_id = None;
9853 let multibuffer = cx.add_model(|cx| {
9854 let mut multibuffer = MultiBuffer::new(0);
9855 excerpt1_id = multibuffer
9856 .push_excerpts(
9857 buffer.clone(),
9858 [
9859 ExcerptRange {
9860 context: Point::new(0, 0)..Point::new(1, 4),
9861 primary: None,
9862 },
9863 ExcerptRange {
9864 context: Point::new(1, 0)..Point::new(2, 4),
9865 primary: None,
9866 },
9867 ],
9868 cx,
9869 )
9870 .into_iter()
9871 .next();
9872 multibuffer
9873 });
9874 assert_eq!(
9875 multibuffer.read(cx).read(cx).text(),
9876 "aaaa\nbbbb\nbbbb\ncccc"
9877 );
9878 let (_, editor) = cx.add_window(Default::default(), |cx| {
9879 let mut editor = build_editor(multibuffer.clone(), cx);
9880 let snapshot = editor.snapshot(cx);
9881 editor.begin_selection(Point::new(1, 3).to_display_point(&snapshot), false, 1, cx);
9882 assert_eq!(
9883 editor.selections.ranges(cx),
9884 [Point::new(1, 3)..Point::new(1, 3)]
9885 );
9886 editor
9887 });
9888
9889 multibuffer.update(cx, |multibuffer, cx| {
9890 multibuffer.remove_excerpts([&excerpt1_id.unwrap()], cx);
9891 });
9892 editor.update(cx, |editor, cx| {
9893 assert_eq!(
9894 editor.selections.ranges(cx),
9895 [Point::new(0, 0)..Point::new(0, 0)]
9896 );
9897
9898 // Ensure we don't panic when selections are refreshed and that the pending selection is finalized.
9899 editor.change_selections(None, cx, |s| {
9900 s.refresh();
9901 });
9902 assert_eq!(
9903 editor.selections.ranges(cx),
9904 [Point::new(0, 3)..Point::new(0, 3)]
9905 );
9906 assert!(editor.selections.pending_anchor().is_some());
9907 });
9908 }
9909
9910 #[gpui::test]
9911 async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
9912 cx.update(|cx| cx.set_global(Settings::test(cx)));
9913 let language = Arc::new(
9914 Language::new(
9915 LanguageConfig {
9916 brackets: vec![
9917 BracketPair {
9918 start: "{".to_string(),
9919 end: "}".to_string(),
9920 close: true,
9921 newline: true,
9922 },
9923 BracketPair {
9924 start: "/* ".to_string(),
9925 end: " */".to_string(),
9926 close: true,
9927 newline: true,
9928 },
9929 ],
9930 ..Default::default()
9931 },
9932 Some(tree_sitter_rust::language()),
9933 )
9934 .with_indents_query("")
9935 .unwrap(),
9936 );
9937
9938 let text = concat!(
9939 "{ }\n", // Suppress rustfmt
9940 " x\n", //
9941 " /* */\n", //
9942 "x\n", //
9943 "{{} }\n", //
9944 );
9945
9946 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
9947 let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
9948 let (_, view) = cx.add_window(|cx| build_editor(buffer, cx));
9949 view.condition(&cx, |view, cx| !view.buffer.read(cx).is_parsing(cx))
9950 .await;
9951
9952 view.update(cx, |view, cx| {
9953 view.change_selections(None, cx, |s| {
9954 s.select_display_ranges([
9955 DisplayPoint::new(0, 2)..DisplayPoint::new(0, 3),
9956 DisplayPoint::new(2, 5)..DisplayPoint::new(2, 5),
9957 DisplayPoint::new(4, 4)..DisplayPoint::new(4, 4),
9958 ])
9959 });
9960 view.newline(&Newline, cx);
9961
9962 assert_eq!(
9963 view.buffer().read(cx).read(cx).text(),
9964 concat!(
9965 "{ \n", // Suppress rustfmt
9966 "\n", //
9967 "}\n", //
9968 " x\n", //
9969 " /* \n", //
9970 " \n", //
9971 " */\n", //
9972 "x\n", //
9973 "{{} \n", //
9974 "}\n", //
9975 )
9976 );
9977 });
9978 }
9979
9980 #[gpui::test]
9981 fn test_highlighted_ranges(cx: &mut gpui::MutableAppContext) {
9982 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
9983
9984 cx.set_global(Settings::test(cx));
9985 let (_, editor) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
9986
9987 editor.update(cx, |editor, cx| {
9988 struct Type1;
9989 struct Type2;
9990
9991 let buffer = buffer.read(cx).snapshot(cx);
9992
9993 let anchor_range = |range: Range<Point>| {
9994 buffer.anchor_after(range.start)..buffer.anchor_after(range.end)
9995 };
9996
9997 editor.highlight_background::<Type1>(
9998 vec![
9999 anchor_range(Point::new(2, 1)..Point::new(2, 3)),
10000 anchor_range(Point::new(4, 2)..Point::new(4, 4)),
10001 anchor_range(Point::new(6, 3)..Point::new(6, 5)),
10002 anchor_range(Point::new(8, 4)..Point::new(8, 6)),
10003 ],
10004 |_| Color::red(),
10005 cx,
10006 );
10007 editor.highlight_background::<Type2>(
10008 vec![
10009 anchor_range(Point::new(3, 2)..Point::new(3, 5)),
10010 anchor_range(Point::new(5, 3)..Point::new(5, 6)),
10011 anchor_range(Point::new(7, 4)..Point::new(7, 7)),
10012 anchor_range(Point::new(9, 5)..Point::new(9, 8)),
10013 ],
10014 |_| Color::green(),
10015 cx,
10016 );
10017
10018 let snapshot = editor.snapshot(cx);
10019 let mut highlighted_ranges = editor.background_highlights_in_range(
10020 anchor_range(Point::new(3, 4)..Point::new(7, 4)),
10021 &snapshot,
10022 cx.global::<Settings>().theme.as_ref(),
10023 );
10024 // Enforce a consistent ordering based on color without relying on the ordering of the
10025 // highlight's `TypeId` which is non-deterministic.
10026 highlighted_ranges.sort_unstable_by_key(|(_, color)| *color);
10027 assert_eq!(
10028 highlighted_ranges,
10029 &[
10030 (
10031 DisplayPoint::new(3, 2)..DisplayPoint::new(3, 5),
10032 Color::green(),
10033 ),
10034 (
10035 DisplayPoint::new(5, 3)..DisplayPoint::new(5, 6),
10036 Color::green(),
10037 ),
10038 (
10039 DisplayPoint::new(4, 2)..DisplayPoint::new(4, 4),
10040 Color::red(),
10041 ),
10042 (
10043 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
10044 Color::red(),
10045 ),
10046 ]
10047 );
10048 assert_eq!(
10049 editor.background_highlights_in_range(
10050 anchor_range(Point::new(5, 6)..Point::new(6, 4)),
10051 &snapshot,
10052 cx.global::<Settings>().theme.as_ref(),
10053 ),
10054 &[(
10055 DisplayPoint::new(6, 3)..DisplayPoint::new(6, 5),
10056 Color::red(),
10057 )]
10058 );
10059 });
10060 }
10061
10062 #[gpui::test]
10063 fn test_following(cx: &mut gpui::MutableAppContext) {
10064 let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
10065
10066 cx.set_global(Settings::test(cx));
10067
10068 let (_, leader) = cx.add_window(Default::default(), |cx| build_editor(buffer.clone(), cx));
10069 let (_, follower) = cx.add_window(
10070 WindowOptions {
10071 bounds: WindowBounds::Fixed(RectF::from_points(vec2f(0., 0.), vec2f(10., 80.))),
10072 ..Default::default()
10073 },
10074 |cx| build_editor(buffer.clone(), cx),
10075 );
10076
10077 let pending_update = Rc::new(RefCell::new(None));
10078 follower.update(cx, {
10079 let update = pending_update.clone();
10080 |_, cx| {
10081 cx.subscribe(&leader, move |_, leader, event, cx| {
10082 leader
10083 .read(cx)
10084 .add_event_to_update_proto(event, &mut *update.borrow_mut(), cx);
10085 })
10086 .detach();
10087 }
10088 });
10089
10090 // Update the selections only
10091 leader.update(cx, |leader, cx| {
10092 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
10093 });
10094 follower.update(cx, |follower, cx| {
10095 follower
10096 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10097 .unwrap();
10098 });
10099 assert_eq!(follower.read(cx).selections.ranges(cx), vec![1..1]);
10100
10101 // Update the scroll position only
10102 leader.update(cx, |leader, cx| {
10103 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
10104 });
10105 follower.update(cx, |follower, cx| {
10106 follower
10107 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10108 .unwrap();
10109 });
10110 assert_eq!(
10111 follower.update(cx, |follower, cx| follower.scroll_position(cx)),
10112 vec2f(1.5, 3.5)
10113 );
10114
10115 // Update the selections and scroll position
10116 leader.update(cx, |leader, cx| {
10117 leader.change_selections(None, cx, |s| s.select_ranges([0..0]));
10118 leader.request_autoscroll(Autoscroll::Newest, cx);
10119 leader.set_scroll_position(vec2f(1.5, 3.5), cx);
10120 });
10121 follower.update(cx, |follower, cx| {
10122 let initial_scroll_position = follower.scroll_position(cx);
10123 follower
10124 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10125 .unwrap();
10126 assert_eq!(follower.scroll_position(cx), initial_scroll_position);
10127 assert!(follower.autoscroll_request.is_some());
10128 });
10129 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0]);
10130
10131 // Creating a pending selection that precedes another selection
10132 leader.update(cx, |leader, cx| {
10133 leader.change_selections(None, cx, |s| s.select_ranges([1..1]));
10134 leader.begin_selection(DisplayPoint::new(0, 0), true, 1, cx);
10135 });
10136 follower.update(cx, |follower, cx| {
10137 follower
10138 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10139 .unwrap();
10140 });
10141 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..0, 1..1]);
10142
10143 // Extend the pending selection so that it surrounds another selection
10144 leader.update(cx, |leader, cx| {
10145 leader.extend_selection(DisplayPoint::new(0, 2), 1, cx);
10146 });
10147 follower.update(cx, |follower, cx| {
10148 follower
10149 .apply_update_proto(pending_update.borrow_mut().take().unwrap(), cx)
10150 .unwrap();
10151 });
10152 assert_eq!(follower.read(cx).selections.ranges(cx), vec![0..2]);
10153 }
10154
10155 #[test]
10156 fn test_combine_syntax_and_fuzzy_match_highlights() {
10157 let string = "abcdefghijklmnop";
10158 let syntax_ranges = [
10159 (
10160 0..3,
10161 HighlightStyle {
10162 color: Some(Color::red()),
10163 ..Default::default()
10164 },
10165 ),
10166 (
10167 4..8,
10168 HighlightStyle {
10169 color: Some(Color::green()),
10170 ..Default::default()
10171 },
10172 ),
10173 ];
10174 let match_indices = [4, 6, 7, 8];
10175 assert_eq!(
10176 combine_syntax_and_fuzzy_match_highlights(
10177 &string,
10178 Default::default(),
10179 syntax_ranges.into_iter(),
10180 &match_indices,
10181 ),
10182 &[
10183 (
10184 0..3,
10185 HighlightStyle {
10186 color: Some(Color::red()),
10187 ..Default::default()
10188 },
10189 ),
10190 (
10191 4..5,
10192 HighlightStyle {
10193 color: Some(Color::green()),
10194 weight: Some(fonts::Weight::BOLD),
10195 ..Default::default()
10196 },
10197 ),
10198 (
10199 5..6,
10200 HighlightStyle {
10201 color: Some(Color::green()),
10202 ..Default::default()
10203 },
10204 ),
10205 (
10206 6..8,
10207 HighlightStyle {
10208 color: Some(Color::green()),
10209 weight: Some(fonts::Weight::BOLD),
10210 ..Default::default()
10211 },
10212 ),
10213 (
10214 8..9,
10215 HighlightStyle {
10216 weight: Some(fonts::Weight::BOLD),
10217 ..Default::default()
10218 },
10219 ),
10220 ]
10221 );
10222 }
10223
10224 fn empty_range(row: usize, column: usize) -> Range<DisplayPoint> {
10225 let point = DisplayPoint::new(row as u32, column as u32);
10226 point..point
10227 }
10228
10229 fn assert_selection_ranges(
10230 marked_text: &str,
10231 selection_marker_pairs: Vec<(char, char)>,
10232 view: &mut Editor,
10233 cx: &mut ViewContext<Editor>,
10234 ) {
10235 let snapshot = view.snapshot(cx).display_snapshot;
10236 let mut marker_chars = Vec::new();
10237 for (start, end) in selection_marker_pairs.iter() {
10238 marker_chars.push(*start);
10239 marker_chars.push(*end);
10240 }
10241 let (_, markers) = marked_text_by(marked_text, marker_chars);
10242 let asserted_ranges: Vec<Range<DisplayPoint>> = selection_marker_pairs
10243 .iter()
10244 .map(|(start, end)| {
10245 let start = markers.get(start).unwrap()[0].to_display_point(&snapshot);
10246 let end = markers.get(end).unwrap()[0].to_display_point(&snapshot);
10247 start..end
10248 })
10249 .collect();
10250 assert_eq!(
10251 view.selections.display_ranges(cx),
10252 &asserted_ranges[..],
10253 "Assert selections are {}",
10254 marked_text
10255 );
10256 }
10257}
10258
10259trait RangeExt<T> {
10260 fn sorted(&self) -> Range<T>;
10261 fn to_inclusive(&self) -> RangeInclusive<T>;
10262}
10263
10264impl<T: Ord + Clone> RangeExt<T> for Range<T> {
10265 fn sorted(&self) -> Self {
10266 cmp::min(&self.start, &self.end).clone()..cmp::max(&self.start, &self.end).clone()
10267 }
10268
10269 fn to_inclusive(&self) -> RangeInclusive<T> {
10270 self.start.clone()..=self.end.clone()
10271 }
10272}