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