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