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