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