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