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