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